Apeller des fonctions C++ (via une dll) depuis C#

Résolu
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009 - 5 déc. 2008 à 15:42
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009 - 11 déc. 2008 à 15:34
Bonjour,

bien que je ne sois pas du tout expérimenté dans ces langages, on m'a demandé depuis une application C# d'apeller des fonction C++ qui seront contenu dans une DLL.
A la base je suis plutôt Java, donc C# je m'en sort encore pour ce que j'ai eu à faire (webservice), mais le C++ on peut dire que j'y connais rien.

Alors j'ai 2 questions :

- Comment faire l'appel à ces fonctions ?
- Comment ça se passe pour les type de données pour les paramètres/retour ? Je peux utiliser des tableaux et structures ?

Merci pour votre aide.
A voir également:

23 réponses

Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
8 déc. 2008 à 23:10
Salut, coté C# tu réécris la structure et tu la passes par référence ( ref maStruct ou out maStruct )  pour simuler un pointeur.. mais dans les paramêtres de ta fonction il y a aussi des doubles pointeurs, là c'est moins simple, il faut utiliser une référence sur un IntPtr ( ref monIntPtr ) puis marshaller le IntPtr en Struct ( Marshal.PtrToStructure( ) ). Ou tu codes en unsafe ce qui te permetra d'utiliser des pointeurs et des doubles pointeurs en C#.
3
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
9 déc. 2008 à 14:46
Pour passer un tableau  :


// Prototype C++
// void GetInfo( MEDIA_INFO* pInfos, int nbSize );


[ StructLayout( LayoutKind.Sequential ) ]
public struct MEDIA_INFO
{
    public int width;
    public int height;
}


[ DllImport( ".." ) ]
public static extern void GetInfo( [ In, Out ] MEDIA_INFO[ ] pInfos, int nbSize );
3
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
10 déc. 2008 à 01:47
Les types blittables ont la même disposition en mémoire managé et non-managée ( int, float etc.. et struct contenant uniquement des types blittables ).

J'ai pas testé, ça devrait marcher mais le principe est là :

// Prototype C++
//void GetFaces( FACE_INFO** pFaces, unsigned int* pSize );


[ StructLayout( LayoutKind.Sequential ) ]
public struct FACE_INFO
{
    public int x;
    public int y;
}


[ DllImport( ".." ) ]
public static extern void GetFaces( out IntPtr pFaces, out int pSize );


public void GetFaces( )
{
    IntPtr ptrFaces = IntPtr.Zero;
    int size = 0;
    int offset = Marshal.SizeOf( typeof( FACE_INFO ) );


    GetFaces( out ptrFaces, out size );


    // Pour éviter une copie, utilise du code unsafe,
    // tu auras accès directement aux pointeurs.
    FACE_INFO[ ] copy = new FACE_INFO[ size ];
    for ( int i = 0; i < size; i++ )
    {
        IntPtr ptr = ( IntPtr )( ( long )ptrFaces + ( offset * i ) );
        Marshal.PtrToStructure( ptr, copy[ i ] );
    }


    // Ici tu dois appeller une méthode qui libère le tableau alloué coté C++.
    // .NET ne peut pas libérer de la mémoire non-managée, sauf si elle est
    // allouée via CoTaskMemAlloc/CoTaskMemFree.


    // Ou alors tu alloues le tableau coté .NET ce qui nécessite de demander
    // la taille du futur tableau avant l'appel, genre tu passes NULL et la fonction
    // C++ te renvoie la taille nécessaire, dans ce cas Le IntPtr devra être
    // initialisé avec l'adresse du tableau.


}
3
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
10 déc. 2008 à 13:29
Si je comprends bien, que ce soit pour le pointeur sur le tabeau m_Buffer à l'intérieur d'une struct et le pointeur sur structure m_Relations lui aussi dans une struct, ce sont des paramêtres en sortie qui donc ne nécessitent pas de pré-allouer la mémoire coté C# avant l'appel à la fonction non-managée, exact ? Dans ce cas un simple IntPtr suffit pour remplacer un pointeur C++ dans les déclarations, l'intérêt du IntPtr c'est qu'il sera sur 32bits ou 64 bits selon la platforme, comme un vrai pointeur.

Pour les struct tu as compris, c'est Marshal.PtrToStructure pour copier une struct non-managée vers une struct managée et pour un tableau c'est Marshal.Copy.

Mais vu que tu n'es pas habitué au "marshalling" et que ta fonction est quand même bien compliquée, je te conseille de ne rien convertir et d'utiliser les pointeurs, ce genre de code est autorisé en C# :

[ DllImport( ".." ) ]
private static extern unsafe void ExtractFaceRelation
(
    MEDIA_INFO* imagesList,
    FACE_INFO** facesList,
    uint* nbFaces,
    FACE_RELATION** relationsList,
    uint* nbRelations
);







private unsafe void Exemple( )
{
    MEDIA_INFO info;
    info.width = 640;



    MEDIA_INFO* pInfo = &info;
    pInfo->height = 480;
}
3

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

Posez votre question
billou_13 Messages postés 860 Date d'inscription jeudi 4 mars 2004 Statut Membre Dernière intervention 19 août 2014 29
5 déc. 2008 à 17:08
Bonsoir,

Quelques recherches sur le forum et le net font parfois des miracles:
- http://faqcsharp.developpez.com/?page=syst#syst_apiinvoke
- http://www.csharpfr.com/codes/UTILISER-LIBRAIRIE-CPLUSPLUS-NON-MANAGE-CSHARP-MANAGE_26231.aspx
- http://www.csharpfr.com/codes/PINVOKE-COMMENT-INVOQUER-DLL-PARTIR-CSHARP-NET_11491.aspx

Bon codage,

Billou_13
Bask En Force

--------------------------------------------------------------------
Connaître la réponse est une chose, savoir pourquoi en est une autre
---------------------
0
billou_13 Messages postés 860 Date d'inscription jeudi 4 mars 2004 Statut Membre Dernière intervention 19 août 2014 29
5 déc. 2008 à 17:12
Encore une petite foire de liens histoire de:
- http://www.csharpfr.com/forum/sujet-Sous%20avec%20des%20DLLImport_747879.aspx
- http://msdn.microsoft.com/en-us/magazine/cc164123.aspx
- http://www.codeproject.com/KB/cs/essentialpinvoke.aspx

PS: trouver via le forum, merci aux liens de Bidou et MorpionMx

Billou_13
Bask En Force

--------------------------------------------------------------------
Connaître la réponse est une chose, savoir pourquoi en est une autre
---------------------
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
5 déc. 2008 à 19:14
Merci pour ces infos (notamment le dernier).

J'avais effectivement déjà trouvé des truc sur le DllImport , mais c'était plus la question des types qui me posait problème.

J'ai trouvé un bon article sur le site de microsoft, et avec tes liens en complément ça devrait m'aider :)
Je pourrais voir tout ça lundi quand je serai de retour au boulot :p

Sur ce bon week-end :D
0
billou_13 Messages postés 860 Date d'inscription jeudi 4 mars 2004 Statut Membre Dernière intervention 19 août 2014 29
6 déc. 2008 à 01:13
Bonsoir,

Si ton lien est intéressant, tu auras gagné le droit de le partager avec la communauté codes-sources ^^

Bonne nuit et bon courage,


Billou_13
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
6 déc. 2008 à 09:15
J'y ai bien sur pensé mais je n'étais déjà plus a mon travail, et ne l'avait donc pas sous la main ;)

A voir lundi!
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
8 déc. 2008 à 09:44
Voici la lien que j'avais trouvé, qui me semble plutôt complet!

http://msdn.microsoft.com/en-us/library/aa288468.aspx
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
8 déc. 2008 à 10:44
Bon,

je suis toujours en train de lire les différentes doc, mais je n'ai pas l'instant pas la réponse clairement à cette question :

si je passe en parametre des objets/struct C# marshallé en pointeur, si ma fonction C++ les modifies, les changements seront t'il répercuté coté C# ?
Où suis-je obligé de me servir du retour de la fonction ?

Car on vient de me fournir le prototype de la fonction cpp, et je n'ai pas de retour.

MEDIASEARCHDLL_API

void ExtractFaceRelation(
MEDIA_INFO* imagesList,
// inunsigned
int nbImages,
// in
FACE_INFO** facesList,
// out
unsigned
int* nbFaces,
// out
FACE_RELATION** relationsList,
// out
unsigned
int* nbRelations);
// out
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
9 déc. 2008 à 09:57
Merci pour tes suggestions.
Dans mes tentatives hier j'avais essayé de contourner un peu le problème en encapsulant ça dans d'autre structure. Je n'ai pas encore pu tester (j'ai des souci avec la dll pour le moment), mais dans l'idée ça marcherai ?

Si je prend l'exemple suivant :
 MEDIA_INFO * imagesList
unsigned int nbImages

Donc en gros une List<MEDIA_INFO> si on avait la même chose en C#.

Comme je ne savais par faire 2 marshalling a la suite, j'ai créé une autre structure :
[

StructLayout(
LayoutKind.Sequential)]

private
class LIST_MEDIA_INFO{
    [
MarshalAs(
UnmanagedType.LPArray)]

    public MEDIA_INFO[] listMediaInfo;

    public
uint nbImages;
}

Avec dans l'appel de la fonction :
[
In,
MarshalAs(
UnmanagedType.LPStruct)] LIST_MEDIA_INFO imageList

J'ai un peu la meme chose pour les autres truc du coup.
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
9 déc. 2008 à 10:28
Je précise que j'ai une main mise sur le code cpp, et que je peux donc adapter les protoypes en conséquence.


 


 
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
9 déc. 2008 à 16:29
Merci,

grâce à toi je ne suis pas loin de réussir ! :o
Pas de problème donc pour le tableau, avec la solution que tu m'a donné.
Pas de problème non plus pour remonter des int *

Me reste donc mes "Objet **".
Le but est de remonté un tableau dont je ne connais pas la taille.

J'ai été tenté d'appliquer la méthode du tableau avec un "ref", sauf que ne connaissant pas la taille je ne crois pas que ce soit possible.

Et avec le IntPtr, je ne m'en sort pas bien (manque de pratique du langage)
Avec quoi j'initiliase cet objet ?

J'ai fait :
...
uint nbFaces = 0;
IntPtr pointeur =
new
IntPtr();
methodeDeMaDLL(... ,
ref pointeur,
ref nbFaces);


FACE_INFO[] faces = new FACE_INFO[nbFaces];
Marshal.PtrToStructure(pointeur,faces);

Mais j'ai l'erreur suivante :
La structure spécifiée doit être blittable ou avoir des informations de disposition.

Un dernier petit coup de main m'aiderai énormement!


Merci


 
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
10 déc. 2008 à 07:44
Merci vraiment de ton aide!!

J'essaie ça dès que j'arrive au boulot.

Merci merci merci! :)
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
10 déc. 2008 à 11:32
Bon, j'ai toujours un petit souci...:(
Vraiment ça me donne du mal!

Le problème de ma structure FACE_INFO c'est que dedans j'ai aussi un tableau de taille variable :

[

StructLayout(
LayoutKind.Sequential)]

private
class
FACE_INFO{

public
uint m_XMin;

public
uint m_XMax;

public
uint m_YMin;

public
uint m_YMax;

public
uint m_ImageIndex;

public
byte[] m_Buffer;

public
uint m_BufferSize;
}

Cela doit donc poser problème pour le Marshal.SizeOf(type). 
Il est éventuellement envisageable que j'obtiennent une taille maximale pour ce tableau si ça peut aider.
 
Par contre, même si je ne touche pas au tableau  de byte coté C++, que j'essaie simplement de faire dedans:

(*nbFaces) = 10;
(*facesList) =
new FACE_INFO[10];

for (
int i = 0; i < (*nbFaces); i++){
   (*facesList)[i].m_ImageIndex = i;

   //(*facesList)[i].m_Buffer = new unsigned char[100];
   (*facesList)[i].m_BufferSize = 100;
}

Je me retrouve avec l'erreur "Tentative de lecture ou d'écriture de mémoire protégée" lors de l"opération   Marshal.PtrToStructure(ptr, faces[i]);

Alors je me suis dit que c'était'est peut-être lié au problème du tableau, et apres verification (je l'ai retiré de la structure des 2 cotés) ça venait effectivement de la. (même si je met une taille fixe)

Donc pour le moment j'ai :

Code coté C# :

MEDIA_INFO

[] media =
new
MEDIA_INFO[10];

uint nbImages = 10;

IntPtr ptrFaces =
IntPtr.Zero;

int nbFaces = 0;

int offset =
Marshal.SizeOf(
typeof(
FACE_INFO));ExtractFaceRelation(media, nbImages,

ref ptrFaces,
ref nbFaces);

FACE_INFO[] faces =
new
FACE_INFO[nbFaces];

for (
int i = 0; i < nbFaces; i++){

   IntPtr ptr = (
IntPtr)((
long)ptrFaces + (offset * i));
   faces[i] =
new
FACE_INFO();

   Marshal.PtrToStructure(ptr, faces[i]);

}[

DllImport(
@"MediaSearchDllTest.dll",
 EntryPoint =
[mailto:?ExtractFaceRelation@@YAXPAUMEDIA_INFO@@IPAPAUFACE_INFO@@PAI@Z ?ExtractFaceRelation@@YAXPAUMEDIA_INFO@@IPAPAUFACE_INFO@@PAI@Z])]

private
static
extern
void ExtractFaceRelation(
   [
In,
Out]
MEDIA_INFO[] imagesList,

   uint nbImages,
   [
In,
Out]
ref
IntPtr monPointeur,
   [
In,
Out]
ref
int nbFaces

);[

StructLayout(
LayoutKind.Sequential)]

private
class
FACE_INFO
{

public
uint m_XMin;

public
uint m_XMax;

public
uint m_YMin;

public
uint m_YMax;

public
uint m_ImageIndex;

public
byte[] m_Buffer;   //solution à trouver :s

public
uint m_BufferSize;}

Coté C++ : 
struct

FACE_INFO{

unsigned
int m_XMin;

unsigned
int m_XMax;

unsigned
int m_YMin;

unsigned
int m_YMax;

unsigned
int m_ImageIndex;

unsigned
char * m_Buffer;
// data
unsigned
int m_BufferSize;
} ;
__declspec

(
dllexport)
void ExtractFaceRelation(
      MEDIA_INFO* imagesList,
// in
      unsigned
int nbImages,
// in
      FACE_INFO** facesList,
// out
      unsigned
int* nbFaces)
// out
{
...
}
 
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
10 déc. 2008 à 11:53
Arf, et puis je vais avoir un probleme sur le dernier param en sortie de ma fonciton du type FACE_RELATION** puisque j'ai un tableau de struct dedans...

 struct FACE_RELATION{

   struct RELATION_INFO* m_Relations;

   unsigned
int m_NbRelations;
} ;

struct

RELATION_INFO{

   unsigned
int m_FaceIndex;

   unsigned
int m_MediaIndex;
} ;

Je maudit mon chef qui m'a lancé la dessus sans que j'y connaisse rien :s
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
10 déc. 2008 à 14:09
Merci de ta réponse qui m'aide encore uen fois a avancer.

J'étais justement en train de 'jouer' avec les IntPtr pour l'histoire de mon tableau de byte, et comme tu m'a indiqué la bonne méthode (Marshal.Copy(..)) j'ai réussi à récuper ce tableau.

Je vais tenter d'appliquer les principes acquis sur mon dernier paramètre!

Si vraiment je craque j'appliquerai ta derniere solution qui est....simplissime et  que j'aurai adopté tout de suite si j'avais su :D
Mais au point ou j'en suis rendu je ne lacherai pas le morceau si facilement :p

Au moins j'aurai appris des choses ces derniers jours ^_^
0
jmengelle Messages postés 21 Date d'inscription vendredi 28 avril 2006 Statut Membre Dernière intervention 20 janvier 2009
10 déc. 2008 à 15:31
Bingo!

J'ai donc réussi a lire via les Marshall.PtrToStructure et Marshall.Copy et des IntPtr dans tout les ses mes données.

:)

Bon, c'est un peu le bordel quand même, mais ça marche.

J'ai voulu essayé la version "unsafe", mais j'ai eu une erreur du genre :
Cannot take the address of, get the size of, or declare a pointer to a managed type
sur chacun de mes paramètres.

Je pense de tute façon qu'il est préférable de passer via toute les manipulation que j'ai faite ? (d'un point de vue sécuritaire)

En tout cas, encore merci  pour ton aide et ta patience qui fut précieuse!
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
10 déc. 2008 à 16:18
Si ça marche tant mieux : )

"Cannot take the address of, get the size of, or declare a pointer to a managed type.."

Tu déclares tes types avec une classe et moi avec une structure, en C# classe et struct c'est différent, les structures sont des types valeurs alloués sur la pile ( même avec un new ) et les classe des types références alloués sur le tas.

C# utilise un garbage collector qui peut à tout moment déplacer les objets en mémoire, c'est pour ça qu'on ne peut pas prendre l'adresse d'un types référence sur le tas.. bien qu'il soit quand même possible de "fixer" certains objets en mémoire.
0
Rejoignez-nous