2010-01-11 6 views
3

Je suis coincé avec un problème avec Mono Interop sous Linux. J'ai une bibliothèque partagée native (faite dans Lazarus) que j'ai besoin d'utiliser dans un assemblage C#. La bibliothèque partagée est utilisée par un assemblage Mono C# sous Windows et Linux.Crash DLLImport dans Mono (linux): icall_wrapper_mono_marshal_free

L'assembly charge la bibliothèque partagée via DllImport à l'exécution et appelle une fonction exportée qui génère un fichier et renvoie une chaîne avec le nom du nouveau fichier. La fonction dans la bibliothèque partagée fonctionne correctement, mais sous Linux, le runtime Mono se bloque sur Object .__ icall_wrapper_mono_marshal_free lorsque la fonction retourne. Cela fonctionne très bien en utilisant Mono sur Windows.

J'ai effectué plusieurs tests et je peux dire que la bibliothèque partagée fonctionne réellement (le nouveau fichier est généré au chemin spécifié), mais à la fin, le moteur d'exécution se bloque. Il semble que l'exécution ait des problèmes avec le marshaling de la chaîne résultante à l'assemblée, parce que cela fonctionne si j'utilise une fonction exportée qui retourne un int.

Ma bibliothèque partagée est quelque chose comme ceci:

library fileProcessing; 

{$mode objfpc}{$H+} 
... 

function ProcessFile(File, ResultPath: PChar): PChar; cdecl; // returns a null-terminated string, with a C ABI calling convention 
var 
    sFile, sPath, sResult: string; 
begin 
    sFile := StrPas(File); 
    sPath := StrPas(ResultPath); 
... 
    sResult := GenerateNewFile(sFile, sPath); // helper function that generates the file and returns its filename 
    Result := stralloc(length(sResult) + 1); 
    Result := strpcopy(Result, sResult); 
end; 
... 

exports ProcessFile name 'ProcessFile'; 

L'appel assemblage C# va comme ceci:

namespace SIG 
{ 
    public class TsigKernel 
    { 
... 
    [DllImport("fileProcessing.so", CharSet=CharSet.Ansi, 
     EntryPoint="ProcessFile", 
     CallingConvention=CallingConvention.Cdecl, SetLastError = true)] 
    private static extern string ex_ProcessFile(string File, string ResultPath); 

    // managed wrapper for the shared library exported function 
    public string ProcessFile(string File, string ResultPath) 
    { 
    return ex_ProcessFile(File, ResultPath); 
    } 
... 
    } 
} 

J'ai essayé plusieurs alternatives (retourne une chaîne native de la fonction exportée, changer la convention d'appel en stdcall sur l'assembly et la bibliothèque partagée, en changeant le jeu de caractères dans DllImport).

Je suis sûr qu'il me manque quelque chose, mais je n'ai rien trouvé sur ce problème sur Google.

Mon accident se lit comme suit:

================================================================= 
Got a SIGABRT while executing native code. This usually indicates 
a fatal error in the mono runtime or one of the native libraries 
used by your application. 
================================================================= 

Stacktrace: 
    at (wrapper managed-to-native) object.__icall_wrapper_mono_marshal_free (intptr) <0x00004> 
    at (wrapper managed-to-native) object.__icall_wrapper_mono_marshal_free (intptr) <0x00004> 
    at (wrapper managed-to-native) SIG.TsigKernel.ex_ProcessFile (string, string) <0x00064> 
    at SIG.TsigKernel.ProcessFile (string, string) <0x00010> 
    at TEST.Form1.Form1_Load (object,System.EventArgs) <0x00047> 
    at System.Windows.Forms.Form.OnLoad (System.EventArgs) <0x00060> 
    at System.Windows.Forms.Form.OnLoadInternal (System.EventArgs) <0x00081> 
    at System.Windows.Forms.Form.OnCreateControl() <0x00051> 
    at System.Windows.Forms.Control.CreateControl() <0x0012e> 
    at System.Windows.Forms.Control.WmShowWindow (System.Windows.Forms.Message&) <0x0010f> 
    at System.Windows.Forms.Control.WndProc (System.Windows.Forms.Message&) <0x00292> 
    at System.Windows.Forms.ScrollableControl.WndProc (System.Windows.Forms.Message&) <0x00013> 
    at System.Windows.Forms.ContainerControl.WndProc (System.Windows.Forms.Message&) <0x00051> 
    at System.Windows.Forms.Form.WndProc (System.Windows.Forms.Message&) <0x0022a> 
    at System.Windows.Forms.Control/ControlWindowTarget.OnMessage (System.Windows.Forms.Message&) <0x0001d> 
    at System.Windows.Forms.Control/ControlNativeWindow.WndProc (System.Windows.Forms.Message&) <0x0002d> 
    at System.Windows.Forms.NativeWindow.WndProc (intptr,System.Windows.Forms.Msg,intptr,intptr) <0x001eb> 
    at System.Windows.Forms.XplatUIX11.SendMessage (intptr,System.Windows.Forms.Msg,intptr,intptr) <0x002ae> 
    at System.Windows.Forms.XplatUIX11.MapWindow (System.Windows.Forms.Hwnd,System.Windows.Forms.WindowType) <0x0019a> 
    at System.Windows.Forms.XplatUIX11.CreateWindow (System.Windows.Forms.CreateParams) <0x00bb4> 
    at System.Windows.Forms.XplatUI.CreateWindow (System.Windows.Forms.CreateParams) <0x0001d> 
    at System.Windows.Forms.NativeWindow.CreateHandle (System.Windows.Forms.CreateParams) <0x00030> 
    at System.Windows.Forms.Control.CreateHandle() <0x0007f> 
    at System.Windows.Forms.Form.CreateHandle() <0x00014> 
    at System.Windows.Forms.Control.CreateControl() <0x0008a> 
    at System.Windows.Forms.Control.SetVisibleCore (bool) <0x00079> 
    at System.Windows.Forms.Form.SetVisibleCore (bool) <0x0021d> 
    at System.Windows.Forms.Control.set_Visible (bool) <0x0002c> 
    at (wrapper remoting-invoke-with-check) System.Windows.Forms.Control.set_Visible (bool) <0x00057> 
    at System.Windows.Forms.Application.RunLoop (bool,System.Windows.Forms.ApplicationContext) <0x001f9> 
    at System.Windows.Forms.Application.Run (System.Windows.Forms.ApplicationContext) <0x00052> 
    at System.Windows.Forms.Application.Run (System.Windows.Forms.Form) <0x00033> 
    at TEST.Program.Main() <0x00044> 
    at (wrapper runtime-invoke) object.runtime_invoke_void (object,intptr,intptr,intptr) <0x0003a> 

Native stacktrace: 
mono [0x80d36a9] 
[0xffffe410] 
[0xffffe430] 
/lib/libc.so.6(gsignal+0x4f) [0xb76430cf] 
/lib/libc.so.6(abort+0x187) [0xb76449e7] 
/lib/libc.so.6 [0xb767f4ed] 
/lib/libc.so.6 [0xb768550b] 
/lib/libc.so.6 [0xb7686de4] 
/lib/libc.so.6(cfree+0x6d) [0xb7689fdd] 
/usr/lib/libglib-2.0.so.0(g_free+0x36) [0xb780d886] 
[0xb6561634] 
[0xb5786a5d] 
[0xb57869d1] 
[0xb57866c8] 
[0xb5786599] 
[0xb578632a] 
[0xb5785f7a] 
[0xb605dbe7] 
[0xb578c7c0] 
[0xb578bbfb] 
[0xb57820c4] 
[0xb578208a] 
[0xb5781eeb] 
[0xb578b95e] 
[0xb578b936] 
[0xb578ac74] 
[0xb5788acf] 
[0xb578c4b3] 
[0xb605eca5] 
[0xb605e0e6] 
[0xb605e069] 
[0xb605ddf0] 
[0xb57804bd] 
[0xb605db43] 
[0xb57938a2] 
[0xb57800a6] 
[0xb57937f5] 
[0xb5793798] 
[0xb577f062] 
[0xb577ee13] 
[0xb577eacc] 
[0xb71ce1f5] 
[0xb71ce26b] 
mono [0x8063552] 

Toutes les idées?

Répondre

2

Il n'est généralement pas recommandé de renvoyer un string à partir d'un appel de méthode native. Veuillez vous référer à la page Interop with Native Libraries (Strings) du Mono pour voir comment gérer votre cas. Si vous connaissez la taille maximale de la chaîne de retour, l'approche de StringBuilder est la bonne façon de procéder.

+0

Merci beaucoup. J'ai vu cette documentation, mais j'ai complètement manqué l'approche de StringBuilder. Cela fonctionne bien maintenant. – Arcturus

0

La question est de savoir ce que stralloc utilise? Dans Mono, le code équivalent utilise g_free sur le blob retourné, qui est mappé sur libc free. Les chances sont que StrAlloc fait quelque chose d'autre.

+0

stralloc est une fonction RTL FreePascal qui alloue une nouvelle chaîne dans le tas à l'aide du gestionnaire de mémoire natif FreePascal. Je ne sais pas si elle utilise libc sous Linux, mais je préfère ne pas jouer avec des détails aussi obscurs pour le moment. Toutefois, le problème a été résolu à l'aide d'un StringBuilder pour renvoyer la valeur de chaîne à Mono à partir de la bibliothèque partagée native, comme l'a suggéré Laurent. – Arcturus

+0

libc malloc/free/realloc peut être trouvé dans l'unité "cmem" que vous devez inclure dans les utilisations de votre programme principal. résultat: = pchar (malloc (longueur (sresult) +1)); –

2

Ce qui se passe, c'est que C# essaie de libérer la mémoire allouée pour les chaînes retournées en utilisant g_free sous Linux ou CoTaskMemFree sous Windows. Alors que vous pouvez utiliser g_alloc ou CoTaskMemAlloc dans votre programme pour allouer de la mémoire, je recommanderais de changer le type de retour en IntPtr dans le C# et d'utiliser Marshal.PtrToStringAuto pour le convertir en chaîne. Vous devrez également libérer la chaîne renvoyée en la transmettant à une autre fonction dans le code non géré.

Code pour FreeText:

function FreeText(Str: PChar); cdecl; // frees previously allocated string, with a C ABI calling convention 
begin 
    strdispose(Str); 
end;