Sous avec des DLLImport [Résolu]

Signaler
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007
-
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007
-
Voilà j'utilise une dll que j'ai développé en C, dans un programme C#.

Quand j'appel une fonction de la dll par DLLImport, au moment ou le programme passe sur l'appel de cette méthode, celà me génére une exception du type "Is not PInvoke compatible"

J'avoue en perdre mon latin, ai-je fait une fausse manip dans l'appel de la fonction

Merci

Z.
A voir également:

38 réponses

Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
32
Je vois pas.. pourtant le marshaling par défaut c'est ANSI.

Ta fonction est bien de type WINAPI ou __stdcall ??


Faudrait tester avec une structure simplifiée au maximum :


 




[ StructLayout( LayoutKind.Sequential ) ]
public struct MyStruct
{
    public IntPtr text; // Juste une chaine passé en IntPtr pour tester.
}


// ..


string s = Marshal.PtrToStringAnsi( myStruct.text );
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
32
J'ai rien pour compiler du C/C++ sur ma machine, essaye ça si tu veux.. j'espère que ça marche :

// C


typedef struct _MYSTRUCT
{
 char text[ 256 ];
} MYSTRUCT;


void MyFunc( MYSTRUCT* pMyStruct )
{
 printf( "\nText: %s\n", pMyStruct->text );
 strcpy( pMyStruct->text, "Sortie" );
}


// C#


[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi ) ]
public struct MyStruct

 [ MarshalAs( UnmanagedType.ByValTStr, SizeConst = 256 ) ]
 public String text;
}


[ DllImport( "..." ) ]
private static extern void MyFunc( ref MyStruct myStruct );


MyStrcut myStruct = new MyStrcut( );
myStruct.text = "Entree";
MyFunc( ref MyStruct );


Console.WriteLine( myStrcut.text ); // devrait afficher Sortie.
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
32
Elle est compliquée ta structure , ça devrait passer :

public enum Enum1 { }
public enum Enum2 { }


[ StructLayout( LayoutKind.Sequential, CharSet = CharSet.Ansi ) ]
public struct MyStruct
{
    [ MarshalAs( UnmanagedType.ByValTStr, SizeConst = 11 ) ]
    public string Name;
    public Enum1 E1;
    public Enum2 E2;
    [ MarshalAs( UnmanagedType.ByValArray, SizeConst = 3 ) ]
    public StructTrame[ ] Trames;
}


[ StructLayout( LayoutKind.Sequential, CharSet = CharSet.Ansi ) ]
public struct StructTrame
{
    [ MarshalAs( UnmanagedType.ByValTStr, SizeConst = 11 ) ]
    public string Trame;
    public bool OK;
}
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
32
Je viens de tester.. J'avais pas installé Dev-C++ depuis 2 ans au moins.

Ca marche parfaitement !!

// CPP



typedef struct _STRUCTTRAME
{
    bool OK;
    char Trame[15];
}STRUCTTRAME;



typedef struct _MYSTRUCT
{
    char nom[11];
    int listeCle;
    int listeEtat;
    int listeDefaut;
    STRUCTTRAME Trames[4];
}MYSTRUCT;



DLLIMPORT void Fill( MYSTRUCT* p )
{
          if ( p != NULL )
          {
           // ..
          }
}



// C#



[ DllImport( "...") ]
private  static extern void Fill( ref MyStruct m );



public enum Enum1 { }
public enum Enum2 { }
public enum Enum3 { }



[ StructLayout( LayoutKind.Sequential, CharSet = CharSet.Ansi ) ]
public struct MyStruct
{
    [ MarshalAs( UnmanagedType.ByValTStr, SizeConst = 11 ) ]
    public string Nom;
    public Enum1 Cle;
    public Enum2 Etat;
    public Enum3 Defaut;
    [ MarshalAs( UnmanagedType.ByValArray, SizeConst = 4 ) ]
    public StructTrame[ ] Trames;
}



[ StructLayout( LayoutKind.Sequential, CharSet = CharSet.Ansi ) ]
public struct StructTrame
{
    [ MarshalAs( UnmanagedType.U1 ) ]
    public bool OK;
    [ MarshalAs( UnmanagedType.ByValTStr, SizeConst = 15 ) ]
    public string Trame;
}


La seule différence avec ton code c'est que j'ai utilisé des int en C à la place des enums, mais ça doit passer de la même manière.

Donc fait attention ta struct en C doit être exactement agencé de la même manière en C#.

Tu n'utilise pas la #pragma pack, par hasard !?
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Modérateur
Dernière intervention
30 octobre 2008
44
Salut
Qu'est-ce qui n'est pas "PInvoke compatible" ? Un type dans la signature de ta Méthode ?
Il nous en faudrait un peu plus (Exception complete, signature de la méthode, declaration pInvoke, ...)

Mx
MVP C# 
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

oui c'est au niveau de la signature que celà coince,

Method's type signature is not PInvoke Compatible

Ma méthode retourne une structutre contenant des enums un bool et un char*

C'est quand j'utilise ce char* que celà me génére une exception

dans mon appli en C# je déclare une structlayout
de la même ossature que celle dans ma dll en c

j'utilise la méthode Importé par dllimport comme suit

struct = getStruct();

C'est une MarshallDirectiveException qui est levée.
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
32
Salut, comme Mx j'aimerais bien voir le prototype C et le prototype managé.. Si je comprends bien tu alloues une structure avec new dans ton code en C et tu la renvois avec return.. Dans ce cas c'est un IntPtr qu'il faut, et il faut aussi mettre en place un mécanisme pour libérer la mémoire non managée.
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

non je n'utilise aucun new dans ma mfc en c dans le .h jé déclare une

la première structure
typedef struct  _maStruct
{
       char* nom;
       enumDefaut def;
       enumEtat etat;
       _structLigne ligne;
}maStruct;

La deusième
typedef struct _structLigne
{
       char* text;
       bool ok;
}structLigne;

le prototype de ma méthode

typedef _maStruct (WINAPI *DLL_Function_GET_Struct(void));

puis dans mon .cpp principale ma méthode

_maStruct GET_Struct(void)
{
       _maStruct struct;
       // traitement sur ma structure
       return struct;
}

Dans mon .cs

je déclare mes structure

[StructLayout(LayoutKind.Sequential)]
public struct _maStruct
{
       public string nom;
       etc....
}

de même pour _structLigne

aprés :

[DllImport(@"..\..\..\release\MADLL.dll")]
public static extern _maStruct GET_Struct();

je l'utilise aprés comme suit :

private void button1_Click(object sender, EventArgs e)
{
        _maStruct struct;
       struct = GET_Struct();
       Console.Writeline(struct.nom);
}
 en rouge se qui génére l'exception

voilà

Z.
      
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
32
Je me demande tout simplement si c'est possible de marshaler une structure de cette manière, je ne suis jamais tombé sur ce cas de figure..
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

Apparement oui j'ai vu des exemples
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

Vous avez des idées pour me donner un coup de main

Z.
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
32
Renvois un pointeur sur ta structure, après c'est très simple à marshaler vers C#.
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

tu peux développer ?
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
32
Normalement je fais comme ça :


// v1.cpp


void DoSomething( MyStruct* p )
{
   if ( p != NULL )
   {
      p->x = 0;
      p->y = 1;
   }
}


// v1.cs


[ DllImport( ".." )]
static extern void DoSomething( ref MyStruct myStruct );


MyStruct myStruct = new MyStruct( );
DoSomething( ref myStruct );


Ou alors


// v2.cpp


MyStruct* DoSomething( )
{
   MyStruct* p = new MyStruct( );
   p->x = 0;
   p->y = 1;


   return p;
}


void Free( MyStruct* p )
{
   if ( p != NULL )
      delete p;
}


// v2.cs


[ DllImport( ".." ) ]
static extern IntPtr DoSomething( );


[ DllImport( ".." ) ]
static extern void Free( IntPtr ptr );


IntPtr ptr = DoSomething( );
MyStrcut myStruct = ( MyStruct )Marshal.PtrToStructure( ... ); // Ou du code unsafe


// ..


Free( ptr );


On peut aussi utiliser CoTaskMemAlloc et FreeCoTaskMem ce qui évite de définir une fonction Free.
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

J'ai toujours un souci de type qd je met un string j'ai toujours mon exception de levée, même si j'utilise ref,  aprés quand je modifie que je met un int àa passe mais me retourne une valeur de int qui n'est pas celle que je veux.
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

je vais tester ce matin mais je suis bien en WINAPI
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

ok mais comment se déclare ma structure du côté de mon .cpp
Messages postés
3246
Date d'inscription
lundi 25 avril 2005
Statut
Modérateur
Dernière intervention
27 octobre 2012
32
Le plus simple possible, juste avec un pointeur char* rien d'autre.

C'est pour voir si au moins ça marche comme ça sinon c'est a plus rien n'y comprendre :/
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

Effectivement ça change quelque chose j'ai plus l'exception mais ma chaine de retour dans ma strucuture(celle que je marshal en string) elle est vide j'essai de l'afficher mais rien vide de chez vide

je fais un

maStruct = GetStruct();

string s = Marshal.PtrToStringAnsi( myStruct.text );

Console.WriteLine(s);

mais myStruct.text est vide (0 pour être plus précis)
Messages postés
81
Date d'inscription
jeudi 2 février 2006
Statut
Membre
Dernière intervention
24 mai 2007

Bon je crois que ça marche merci encore à tous sympa de votre part une fois mon appli terminé je ferais un tuto sur tout ça