Utiliser des dll c++ en C#

Résolu
vanisheur Messages postés 9 Date d'inscription vendredi 18 juillet 2003 Statut Membre Dernière intervention 11 août 2009 - 9 juin 2009 à 12:12
ssgg Messages postés 1 Date d'inscription lundi 4 décembre 2000 Statut Membre Dernière intervention 25 novembre 2010 - 25 nov. 2010 à 15:15
Bonjour,
J'ai vraiment besoin d'aide.

je suis en train d'ecrire un programme en C#, celui-ci utilise des fonctions C++ incluses dans des DLL.
Une partie de mon programme fonctionne correctement mais je suis bloqué sur un point particulier :

La declaration des prototypes des fonctions C++ ne posent pas de probleme, le passage ou la recuperation des parametres fonctionne partiellement.
Le soucis vient du fait que certaines fonctions C++ de la DLL demandent :
Un pointeur vers un tableau de structures
la dll est censé y inscrire des informations mais je n'arrive pas a le faire.
Avant de demander votre aide j'ai parcouru tous les ouvrages que je possede sur le sujet, j'ai cherché sur le nt et sur ce forum mais les techniques proposées ne fonctionnent pas, voici mon code plus en detail :

Importation de la DLL

[

DllImport(
"IPL-One.dll", CharSet =
CharSet.Ansi)]

unsafe
static
extern
bool IPL_GetDeviceList( 

                                                                                 out
_IPL_DEVICE_LIST[] dev_list,

                                                                                 int n_items,
                                                                                 
int *items_found
                                                                                );
La structure comporte deux champs

[

StructLayout(
LayoutKind.Sequential,CharSet =
CharSet.Ansi)]

public
unsafe
struct
_IPL_DEVICE_LIST
{

/*public unsafe fixed char device_code[50];
public unsafe fixed char family_code[20];*/
public
unsafe
string device_code;

public
unsafe
string family_code;
}
Appel de la fonction c++ de la dll

_IPL_DEVICE_LIST[] dev_list2 =
new
_IPL_DEVICE_LIST[290];
succeed = IPL_GetDeviceList(

                                                         out dev_list2,
                                                         1,
                                                         ptr_no_devices
                                                      );
voici ce qui est censé se passer dans cette fonction :
le premier argument est censé etre un pointeur vers un tableau de strucures,
le second est le Nombre d'elements que l'on demande a la fonction de chercher, un int.
le troisieme est le Nombre d'élements qu'elle a effectivement trouvé, un pointeur d'int.

Lorsque j'appelle cette fonction avec 0 en tant que second parametre, je ne demande PAS a la fonction
d'effectuerer le remplissage du tableau (premier argument), dans ce cas tout fonctionne bien et la fonction me retourne 280 dans le troisieme parametre.

Les choses se gatent si je met par exemple 1 comme ici dans le second parametre, la fonction comprend qu'elle doit me remplir 1 element dans le tableau de structure, autrement dit elle doit ecrire dans la premiere structure du tableau dev_list2.
La j'obtient une exception
Il y a plein d'autres fonctions dans cette dll qui fonctionne sans probleme et auquelles je passe des pointeurs simples de variables primaires comme des int mais la comme je me trouve dans un type reference je n'arrive pas a le faire.
s'il vous plait aidez moi, !!!!

Fab

14 réponses

Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
10 juin 2009 à 00:04
Vu que c'est pour un microcontroleur on va éviter tout marshalling et coder en mode unsafe.

// définitions P/Invoke

[ StructLayout( LayoutKind.Sequential ) ]
public unsafe struct IPL_DEVICE_LIST
{
    public fixed sbyte device_code[ 50 ];
    public fixed sbyte family_code[ 20 ];
}


[ DllImport( "IPL-One.dll" ) ]
//[ SuppressUnmanagedCodeSecurity ]
[ return: MarshalAs( UnmanagedType.Bool ) ]
private static extern unsafe bool IPL_GetDeviceList(
    IPL_DEVICE_LIST* device_list, int n_items, int* items_found );

// Bloc unsafe à mettre dans une fonction :

unsafe
{
    int n_items = 2;
    IPL_DEVICE_LIST[ ] device_list = new IPL_DEVICE_LIST[ n_items ];
    fixed ( IPL_DEVICE_LIST* pdevice_list = device_list )
    {
        int items_found = 0;
        bool ret = IPL_GetDeviceList( pdevice_list, n_items, &items_found );
    }
}
3
vanisheur Messages postés 9 Date d'inscription vendredi 18 juillet 2003 Statut Membre Dernière intervention 11 août 2009
9 juin 2009 à 13:51
J'ai commis une legere erreur ci-dessus,
les dll sont ecrites en C, parcontre il y a une programme de demo qui les utilisent et c'est lui qui est ecrit en C++,

Concretement, voici comment ils utilisent la dll dans le code de demo:

void CSampleDlg::OnOpenSession()
{
 int i;


 // IPL CALL
 if(!IPL_StartSession())
 {
  view_ipl_error();
  return;
 }
 AfxMessageBox("Session Opened");


 // Fill the Device List
 IPL_DEVICE_LIST device_list[300];
 int item_found;


 if(!IPL_GetDeviceList(device_list, 300, &item_found))
  view_ipl_error();
 for(i=0; iAddString(device_list[i].device_code);
 }
 if(((CComboBox *)GetDlgItem(IDC_DEVICE_LIST))->GetCount())
  ((CComboBox *)GetDlgItem(IDC_DEVICE_LIST))->SetCurSel(0);


 Flags |= FLG_SET_SESSION;
 UpdateControls();


}

il faut que j'arrive a faire la meme chose mais en C#
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
9 juin 2009 à 16:50
Salut, tes définitions P/Invoke ne sont pas bien claire, par exemple IPL_GetDeviceList je ne vois pas de type ( ni même de parentheses de fin ) .. Pour la structure IPL_DEVICE_LIST je vois que tu as mis en commentaires les tableaux de caractères, mais faut voir si la définition en C prend des chaines inlines ( ansi ou unicode ) ou des pointeurs char car ça change totalement la taille des structures.

Le C# garantit que les structs d'un tableau sont alignées en mémoire donc il est possible de passer le tableau ou un pointeur fixe sur le tableau à du code C/C++.
0
vanisheur Messages postés 9 Date d'inscription vendredi 18 juillet 2003 Statut Membre Dernière intervention 11 août 2009
9 juin 2009 à 19:49
SAlut, merci pour ta reponse.

bon voila ou j'en suis :
je commence a penser que mon probleme se situe au niveau de la structure meme.
celle-ci d'aprés le fournisseur de la dll est tres simple, elle doit comporter 2 tableaux de char dont la taille est precisée :
20 pour le premier et 50 pour le second.
en C# j'ai tenté deux approches, en collant au maximum au tableau de char et en m'adaptant a ce nouveau langage en remplaçant les tableeux de char par des strings.
mais la non plus je n'y arrive pas.
concretement ce qu'il me faut pour me debloquer c'est :

1- reussir a declarer correctement une structure toute simple contenant 2 strings.

2-utiliser cette structure comme modele pour créer un tableau dont chaque element EST cette structure.

3-passer une reference de ce tableau a ma dll.

la dll en question est chargée de piloter en bas niveau (drivers) une sorte de programmateur usb pour microcontroleurs,
j'ai bien avancé car ace stade je communique deja avec l'appareil et il me repond , toujours au travers de pointeurs.
le reste du boulot se fait presque exclusivement en passant et en recuperant ce genre de reference vers un tableau de structures contenant des chaines.
SI quelqu'un parvient a me debloquer , le reste suivra.
S'il vous plait, regardez de plus pret mes 3 etapes et aidez moi a passer a cette dll ce qu'elle me reclame
merci
fab
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
vanisheur Messages postés 9 Date d'inscription vendredi 18 juillet 2003 Statut Membre Dernière intervention 11 août 2009
10 juin 2009 à 10:25
Salut Mike,

je ne te connais pas mais je n'ai pas assez de qualificatif pour te remercier, aprés 1 semaine de galere intense tu m'a vraiment bien depanné et meme formé.
Tu l'a compris, ton code marche a la perfection alors je t'adresse donc ce bout de code :

Lutinore.name = mike;
mike.merci = (long)+++++++++++++++++++++;

bon evidement ça compile pas mais tu auras traduit ma pensée.
t'est pas administrateur pour rien.
fab
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
10 juin 2009 à 10:36
Merci.
0
vanisheur Messages postés 9 Date d'inscription vendredi 18 juillet 2003 Statut Membre Dernière intervention 11 août 2009
10 juin 2009 à 14:28
Je sais c'est encore moi, j'aurai encore besoin de tes lumieres.

j'ai une des fonctions de la dll qui avant ton aide me renvoyer une exception d'acces violation, maintenant elle tourne mais elle me renvoi FALSE sans arret :

cette fonction est censée initialiser l'ouverture du programmateur : on lui passe le numero de serie de l'appareil accompagné d'un ID, la fonction se charge de faire l'init puis nous dis c'est bon.

le N° de serie est 74406,  c'est une autre fonction qui me l'a dit et en plus ça correspond bien a l'etiquette sur l'appareil.
L'ID c'est un numero generique que JE choisis qui servira d'alias pour dire , la je m'adresse a l'apareil N° 1 si bien sur mon id vaut 1.

la aussi il faut passer un pointeur vers un tableau de structures (maintenant je sais faire !!!!)
Donc la structure en langage C de la dll demande ceci :
unsigned int SN;
int ID;

j'ai fais ceci :


[StructLayout(
LayoutKind.Sequential)]




public





unsafe





struct





_IPL_INST_CONFIG




{








public





fixed





byte

S_N[4];



public





fixed





byte

ID[2];}

ensuite, la fonction de la dll demande comme arguments ceci:
IPL_INST_CONFIG *config
int n_items

j'ai fais ceci :

[



DllImport

(


"IPL-One.dll"

)][



return

:


MarshalAs

(


UnmanagedType

.Bool)]



private





static





extern





unsafe





bool

IPL_SetInstrumentsConfiguration(



_IPL_INST_CONFIG

* all_configs,


UInt16

n_items );

enfin vient le bout de code qui me renvoie FALSE sans arret mais sans exceptions :

unsafe{

UInt16 itms = 1;

_IPL_INST_CONFIG[] all_configs =
new
_IPL_INST_CONFIG[1];

fixed (
_IPL_INST_CONFIG* pinst_config = all_configs){

pinst_config[0].S_N[0] = 166;

pinst_config[0].S_N[1] = 34;

pinst_config[0].S_N[2] = 4;

pinst_config[0].S_N[3] = 0;

pinst_config[0].ID[0] = 1;

pinst_config[0].ID[1] = 0;

/*pinst_config[0].S_N[0] = 0;

pinst_config[0].S_N[1] = 4;

pinst_config[0].S_N[2] = 34;

pinst_config[0].S_N[3] = 166;

pinst_config[0].ID[0] = 0;

pinst_config[0].ID[1] = 1;

*/

bool ret = IPL_SetInstrumentsConfiguration(pinst_config, itms);

while (IPL_INST_INFO.driver_busy ==
true){ ; }

textBox1.Text = textBox1.Text +

"INSTRUMENT CONFIGURATION : " + ret.ToString() +
"\r\n\r\n";}

}

pour les chiffres en fait j'ai decouper BInairement 74406 en 4 octets, je pense que l'ordre est correct, pods fort et faible pour que la dll qui le recupere comprenne bien 74406, j'ai tout de meme essayé dans les deux sens mais je n'ai droit qu'a du false.

Mike, peut tu me sortir une bouée une fois de plus ??

fab
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
10 juin 2009 à 15:06
Pourquoi dans la définition de IPL_SetInstrumentConfiguration  
utilises tu un UInt16 ? Sur une machine 32 bits ça doit être un int ( Int32 ) comme dans l autre fonction..

[ StructLayout( LayoutKind.Sequential ) ]
public unsafe struct _IPL_INST_CONFIG
{
 public fixed byte S_N[ 4 ];
 public fixed byte ID[ 2 ];
}

C'est pas correct cette définition, on utilise les tableaux fixes de bytes uniquement pour simuler les chaines de caractères ANSI.

[ StructLayout( LayoutKind.Sequential ) ]
public struct _IPL_INST_CONFIG
{
 public uint SN; // 32bits non signé.
 public int ID; // 32bits
}
0
vanisheur Messages postés 9 Date d'inscription vendredi 18 juillet 2003 Statut Membre Dernière intervention 11 août 2009
10 juin 2009 à 15:57
Je suis embetté , je viens de faire comme tu m'a dit mais je me recupere une exception AccessViolation au moment de l'appel de la fonction, c'est bizzare parcequ'avec les champs de bytes ça le fait pas.

j'ai l'impression que ça tiens a cette difference.
dans un cas les byte[] sont de taille figées et fixed mais avec les uint non
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
10 juin 2009 à 16:34
Si tu as ça en C

unsigned int SN;
int ID;

Tu ne peux pas mettre ça :

[ StructLayout( LayoutKind.Sequential ) ]
public unsafe struct _IPL_INST_CONFIG
{
 public fixed byte SN[ 4 ];
 public fixed byte ID[ 2 ];
}

car le INT qu'il soit unsigned ou pas il fait 16 ou 32 bits mais pas 1 fois 16 bits et une fois 32 bits.

---

Les Int en C# sont de taille fixe et fixe en mémoire donc champs de bits ou Int ça ne changera rien.. regarde tout de même si y'a une directive "pragma pack" dans le header C/C++.
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
10 juin 2009 à 16:44
Si c'est un microcontroleur 16 bits remplace dans toutes les définitions

int ( c ) -> short ( c# ) ou Int16
uint  ( c ) -> ushort ( c# ) ou UInt16

si c'est 32 bits

int ( c ) -> int ( c# ) ou Int32
uint  ( c ) -> uint ( c# ) ou UInt32
0
vanisheur Messages postés 9 Date d'inscription vendredi 18 juillet 2003 Statut Membre Dernière intervention 11 août 2009
10 juin 2009 à 18:15
Encore merci pour ta patience,

j'ai suivi tes directives, tu as absolument raison (encore heureux !!),
En fait puisque ma variable SN doit contenir 74406 je suis obligatoirement en presence de la partie 32 bits de ton message.

voici ce que j'ai fait :
j'ai repris mon code et j'ai juste transformé mes 4 champs de bytes en un Uint32 en commentant l'init de la variable et ça compile.

maintenant mon probleme est trés claire, je me perd dans les indirections :

dans le bloc unsafe ou j'appelle la fonction, il faut auparavent que j'ecrive dans le SN 74406 pour que la dll puisse lire ce chiffre.
La façon dont je le fait genere mon acces violation, peut tu m'aider en corrigeant ce que j'ai fait :
la l'ecriture du 74406 genere l'exception alors que si je repasse en champs de byte elle n'est pas levée :

temporairement pour debugger je ne touche qu'au SN, j'ai laissé l'id pour comparer

[StructLayout(LayoutKind.Sequential)]
public unsafe struct _IPL_INST_CONFIG
{
public UInt32 S_N;
public fixed byte ID[2];
}


unsafe
{
UInt16 itms = 1;
_IPL_INST_CONFIG[] all_configs = new _IPL_INST_CONFIG[1];
fixed (_IPL_INST_CONFIG* pinst_config = all_configs)
{

pinst_config[0].S_N = 74406

bool ret = IPL_SetInstrumentsConfiguration(pinst_config, itms);

while (IPL_INST_INFO.driver_busy == true)
{ ; }

textBox1.Text = textBox1.Text + "INSTRUMENT CONFIGURATION : " + ret.ToString() + "\r\n\r\n";
}
}
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
10 juin 2009 à 19:20
unsigned int SN;
int ID;


Dans la def en C, on a deux INT, tu es OK ?

[StructLayout(LayoutKind.Sequential)]
public unsafe struct _IPL_INST_CONFIG
{
 public UInt32 S_N;
 public fixed byte ID[2];
}

Alors pourquoi dans ta définition en C# tu mets un INT et un SHORT ( 2 bytes = SHORT ) ?
0
ssgg Messages postés 1 Date d'inscription lundi 4 décembre 2000 Statut Membre Dernière intervention 25 novembre 2010
25 nov. 2010 à 15:15
Bonjour,

Je suis en charge d'un "petit" développement en C#. Le but étant d'utiliser une dll C++ dans le code C#. Or, cette dll utilise des pointeurs. Et je n'arrive pas à utiliser les fonctions appelées dans cette dll. Le problème est le passage des pointeurs C++ à C#. Pourriez-vous me donner un coup de main ?

Mon de code C# est le suivant :

- Déclaration de le fonction :

[DllImport("toto.dll")]

public static extern void TT_Initialization(double [] Data, double [] Input, double [] Output, int [] Iesistance, double []Dsistance, int [] IEgeur, double [] DEgeur, int [] Iquage, double [] DPiquage, ref double PrfT, ref double PrfQ, ref double PrfM, ref double PrfD, ref double PrfE, ref double PrfX, ref double PrfY, string [] arMsg, out int iError);

- Utilisation

...
double PrfT,...;

TT_Initialization(Data, Input, Output, Iesistance, Dsistance, IEgeur,DEgeur, Iquage,DPiquage,ref PrfT,ref PrfQ, ref PrfM,ref PrfD,ref PrfE,ref PrfX,ref PrfY,arMsg,ref iError);


CA MARCHE PAS ...

Le problème vient des variables Pr* qui sont définies selon une struture du type :

typedef struct _Tmp {

int Dim;

double* Value;

int DimMax;

} TMP;

Quelqu'un a t'il une idée ?

Pour info, je suis "développeur occasionnel" .

Merci d'avance,

Stéphane.
0
Rejoignez-nous