new delete dll übergabe



  • Hallo miteinander

    Meine Dll's habe ich bis jetzt immer ohne New + Delete gestaltet. Jetzt wird es aber Zeit, dass ich mich auch mit dem ein wenig beschäftige.

    Grundsätzlich übergebe ich die Dll-Werte an C# in etwa so

    int length = getLongString(str);
    
    [DllImport("test.dll")]
    static extern int getLongString(IntPtr str);
    

    Meine Frage nun. Wenn ich in der Dll die Funktion CreateRandomString aufrufe, wird der String der zurückgegeben werden soll mit 'char* c=new char[x]' erzeugt.
    Dieser Wert wird dann an ppStr übergeben und soll zurück an C#.

    Hier muss ich doch noch die Variable ppStr zurücksetzen (delete). Oder eine zusätzliche Funktion einbringen und aus C# zuchmals in die Dll zum löschen?

    EXP32 int _stdcall getLongString(char **ppStr)
    {
    	return CreateRandomString(ppStr);
    	// In der Funktion CreateRandomString wurde eine Variable mit 
    	// 'new' erzeugt und an ppStr übergeben.
    };
    

    Danke für die Antworten.

    wachs



  • Erstmal, alles was angefordert wird muss wieder freigegeben werden.
    Du hast mehrere Möglichkeiten:

    //Methode 1
    EXP32 char* _stdcall CreateRandomString()
    {
        return new char[100]; //Dummy
    }
    
    EXP32 void stdcall FreeString(char* str)
    {
        delete str;
    }
    
    //Methode 2
    EXP32 char* _stdcall CreateRandomString2()
    {
        static char strData[1024];
        //fülle strData, geht aber halt nur wenn der string immer klein genug ist
        return &strData[0];
    }
    
    //Methode 3
    EXP32 void _stdcall CreateRandomString3(char* strData)
    {
        //fülle strData, geht aber halt nur wenn der string immer klein genug ist
    }
    
    //Methode 1
    [DllImport("test.dll")]
    static extern IntPtr CreateRandomString();
    
    [DllImport("test.dll")]
    static extern void FreeString(IntPtr str);
    
    //Methode 2
    [DllImport("test.dll")]
    static extern IntPtr CreateRandomString2();
    
    //Methode 3
    [DllImport("test.dll")]
    static extern void CreateRandomString3(IntPtr str);
    
    void Main()
    {
        //Methode 1, die Dll verwaltet den Speicher
        IntPtr str1 = CreateRandomString();
        string result1 = Marshal.PtrToStringAuto(str1);
        FreeString(str1);
    
        //Methode 2, der String liegt in einem statischen Array vor, muss also nicht freigegeben werden. Der Speicher wird mein nächsten mal einfach wieder verwendet
        IntPtr str2 = CreateRandomString2();
        string result2 = Marshal.PtrToStringAuto(str2);
    
        //Methode 3, C# verwaltet den Speicher
        IntPtr str3 = Marshal.AllocHGlobal(1024);
        CreateRandomString3(str3);
        string result3 = Marshal.PtrToStringAuto(str3);
        Marshal.FreeHGlobal(str3);
    }
    

    Hoffe das ist noch halbwegs übersichtlich. 😃
    Das wären die Optionen die mir jetzt gerade einfallen. Allerdings alles ungetestet, sollte aber gehen. 🙂
    Was genau du verwendest hängt dann immer vom Anwendungsfall ab.



  • Die Variante von DarkShadow44 funktioniert, ist aber mühsam.

    Du kannst statt dessen z.B. auch direkt einen BSTR zurückgeben. Der wird von .NET dann korrekt freigegeben.

    Oder du gibst nen char* bzw. wchar_t* zurück und verwendest auf der C#-Seite dann direkt den Returntyp string . Dafür muss die C++ Funktion den Speicher für den zurückgegebenen String mit CoTaskMemAlloc anfordern und natürlich sicherstellen dass der String nullterminiert ist.
    Die .NET Runtime gibt den String dann entsprechend mit CoTaskMemFree frei.



  • DarkShadow44 schrieb:

    //Methode 3
    [DllImport("test.dll")]
    static extern void CreateRandomString3(IntPtr str);
    

    Einfacher:

    //Methode 3
    [DllImport("test.dll")]
    static extern void CreateRandomString3(StringBuilder str);
    
    void Main()
    {
        //Methode 3, C# verwaltet den Speicher
        StringBuilder sb = new StringBuilder(1024);
        CreateRandomString3(sb);
        string result3 = sb.ToString();
    }
    


  • Vielen dank für die Antworten.

    Die Variante mit dem ' BSTR' werde ich noch ausprobieren. Die erste Variante ist ganz interessant.

    Ich geh davon aus, dass diese Variante nicht legitim und C++-konform ist. Wie auch, hier wird die Deletezeile doch gleich übersprungen.

    //Das hier ist bestimmt nicht legitim oder??
    int getLongString2(char** ppStr,int iStr)
    {	
    	CreateRandomString(_ppStr,iStr);
    	*ppStr=*_ppStr;
    	return std::strlen(*_ppStr);
    	delete [] *ppStr;
    };
    

    Wie sieht es den mit dem aus?

    using System;
    using System.Data;
    using System.Text;
    using System.Diagnostics;
    using System.Collections;
    using Microsoft.VisualBasic;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    
    public class MainClass
    {
    	[DllImport("CreateString.dll", CharSet = CharSet.Ansi)]
    	public static extern int getLongString1(IntPtr ptrStr, int iStrLen);
    	//<DllImport("CreateString.dll", CharSet:=CharSet.Ansi)> _
    	//Public Shared Function getLongString2(ByVal ptrStr As IntPtr, ByVal iStrLen As Integer) As Integer
    	//End Function
    	[DllImport("CreateString.dll", CharSet = CharSet.Ansi)]
    	public static extern bool deleteString();
    	public static void Main()
    	{
    		int iStrLen = 10000000;
    		StringBuilder sbRandom = null;
    
    		IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(IntPtr.Zero));
    		int iStrRnd = getLongString1(ptr, iStrLen);
    		IntPtr ptr0 = Marshal.ReadIntPtr(ptr);
    		sbRandom = new StringBuilder(Marshal.PtrToStringAnsi(ptr0),iStrRnd);
    		Marshal.FreeHGlobal(ptr);
    
    		if (deleteString()) {
    			System.Diagnostics.Debugger.Break();
    		}
    	}
    }
    

    Hier wird die Variable '_ppStr' öffentlich deklariert. Sie weisst später dann der Variable 'ppStr' den Wert zu. Sobald ich in .Net bin, wird anschliessend die Funktion deleteString aufgerufen. Bei mir funktionert das, aber ich Frag mich, ob so nicht trozdem ein Speicherleck produziert wird?

    #pragma once
    
    #ifndef __CreateString_DLL_H__
    #define __CreateString_DLL_H__
    
    #include <iostream>
    #include <windows.h>
    
    #define EXP32  extern "C" __declspec(dllexport)
    EXP32	int		_stdcall getLongString1(char** ppStr,int iStrLen);
    EXP32	bool	_stdcall deleteString();
    #endif
    
    #include "stdafx.h"
    #include <time.h>
    #include <string>
    
    #include "CreateString.h"
    
    char *cS;
    time_t t;
    int random;
    char **_ppStr;
    
    void CreateRandomString(int &iSize)
    {
    	const char alphanum[] ="0123456789"
    						   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    						   "abcdefghijklmnopqrstuvwxyz";
    
    	_ppStr=&cS;
    
    	time (&t);
    	srand((unsigned int)t);
    	int iiMax=std::strlen(alphanum);
    
    	char *pStr=new char[iSize];
    	memset(pStr,0,iSize);
    
    	if(iSize>0){
    		for (int i=0;i<iSize;++i){
    			random=(rand () % iANum);
    			if(random>=iANum){i-=1;continue;}
    			if(random<0){i-=1;continue;}
    			pStr[i] = alphanum[random];
    			if(pStr[i]==0){i-=1;}
    		}
    		*_ppStr=pStr;
    	}
    };
    
    bool _stdcall deleteString()
    {
    	delete [] *_ppStr;
    	return true;
    };
    
    int _stdcall getLongString1(char** ppStr,int iStrLen)
    {	
    	CreateRandomString(iStr);
    	*ppStr=*_ppStr;
    
    	/* Mach noch irgendwas */ 
    
    	return std::strlen(*ppStr);
    };
    

    Edit: Ok das mit dem Timer das stört mich noch. Der wird nach jedem Durchgang aufgerufen. Einmal würde sicher reichen.

    gruss wachs


Log in to reply