2010-05-21 7 views
2

dans un fichier C# i ai données de chaîneP/Invoke une fonction passée StringBuilder

class Archiver { 
    [DllImport("Archiver.dll")] 
    public static extern void archive(string data, StringBuilder response); 
} 

est une entrée, et la réponse StringBuilder est où la fonction écrit quelque chose

le prototype de la fonction d'archivage (écrit en C) ressemble à ceci:

void archive(char * dataChr, char * outChr); 

et il reçoit une chaîne dans dataChr, et fait alors un

strcpy(outChr,"some big text"); 

de C# je l'appelle quelque chose comme ceci:

string message = "some text here"; 
StringBuilder response = new StringBuilder(10000); 
Archiver.archive(message,response); 

cela fonctionne, mais le problème, comme vous pouvez voir est que je donne une valeur à la taille de StringBuilder, mais la fonction d'archivage peut rendre un (moyen) texte plus grand que la taille que j'ai donnée à mon StringBuilder. Aucun moyen de réparer cela?

Répondre

5

Vous devez écrire une fonction qui vous indique la taille du StringBuilder, puis appeler cette fonction pour initialiser le StringBuilder.

+0

Je pensais juste à ce sujet . probablement le meilleur moyen. Bon sang, vous êtes rapides! –

+1

@andrew, vous devriez vraiment envisager de passer la taille de votre tampon dans la fonction d'archivage, cela aidera à prévenir les dépassements de mémoire tampon, l'archive ne doit remplir le tampon que jusqu'à la taille de la mémoire tampon. –

-1

Vous n'avez pas besoin de donner une taille à la réponse.

+1

** FAUX **. Tu fais. – SLaks

+0

J'ai essayé de ne pas donner une taille mais ça plante –

+0

Je pense à lancer une autre fonction qui retournera une valeur int contenant la taille à mettre dans le stringbuilder (je pense avoir vu cette pratique dans les fonctions de windows api) , mais cela ne sonne pas très bien –

2

Contrôlez-vous l'implémentation de la fonction archive? Si vous faites alors vous avez quelques options.

1- La fonction archive alloue le tampon et le renvoie à l'appelant. L'inconvénient est que l'appelant aura besoin d'utiliser la bonne fonction pour libérer le tampon sinon risque de fuite de mémoire et même de corrompre le tas.

2- Passez la taille du tampon que vous avez à la fonction archive afin qu'elle ne dépasse pas la longueur du tampon lors du remplissage du tampon.

3- Si vous pouvez avoir une fonction qui peut retourner la taille de tampon requise, vous pouvez l'utiliser. C'est une approche courante dans l'API Windows, en passant null comme tampon et un pointeur vers un DWORD qui reçoit la taille de tampon requise, que vous pouvez ensuite allouer et faire un second appel en passant le tampon alloué.

0

j'ai eu un problème très similaire une fois, mais j'avais le code source de la dll, donc j'ajouté une fonction qui décharge un fichier, le nouvel appel ressemblait à ceci:

cr012062(query,filename) 

alors je viens de lire le contenu du fichier

File.ReadAllText(filename) 
+0

Ceci est une solution extrêmement pauvre. – SLaks

1

j'aurais le code non managé allouer la mémoire, puis laissez le côté géré le copier dans un tableau géré par un IntPtr et libérer l'allocation.

Vous devez d'abord votre fonction non managée retourner la taille de celui-ci est tableau de sortie:

void archive(char * dataChr, char * outChr, int length); 

Ensuite, le côté géré doit obtenir comme IntPtr:

class Archiver { 
    public static byte[] Archive(string data) { 
     IntPtr responsePtr = IntPtr.Zero; 
     int length = 0; 

     // Call the unmanaged function with our output params 
     archive(data, responsePtr, length); 

     byte[] responseArray; 
     try { 
      // Create an array for the response 
      responseArray = new byte[length]; 
      // Copy from unmanaged into managed 
      Marshal.Copy(responsePtr, responseArray, 0, length); 
     } finally { 
      // Free the unmanaged memory once copied 
      Marshal.FreeHGlobal(responsePtr); 
     } 

     return responseArray; 
    } 

    [DllImport("Archiver.dll")] 
    private static extern void archive(string data, [Out]IntPtr encoded, [Out]int length); 
} 

Vous n'a pas précisé si vos données étaient réellement une chaîne ou si vous utilisiez un stringbuffer pour contenir des données binaires opaques. Si les données de réponse est une chaîne terminée par zéro, alors vous pouvez facilement utiliser PtrToStringUni ou PtrToStringAnsi pour obtenir un string au lieu d'un simple tableau:

Du côté non géré:

void archive(char * dataChr, char * outChr); 

Du côté géré:

class Archiver { 
    public static string Archive(string data) { 
     IntPtr responsePtr = IntPtr.Zero; 

     // Call the unmanaged function with our output params 
     archive(data, responsePtr); 

     string responseString; 
     try { 
      // Marshal the string from unmanaged SZ String 
      responseString = Marshal.PtrToStringAnsi(responsePtr); 
     } finally { 
      // Free the unmanaged memory once copied 
      Marshal.FreeHGlobal(responsePtr); 
     } 

     return responseString; 
    } 

    [DllImport("Archiver.dll")] 
    private static extern void archive(string data, [Out]IntPtr encoded); 
} 

NB: Je n'ai pas testé tout de ce code, donc il peut y avoir quelques petites omissions ou des bugs ...