IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

P/Invoke sur fonction native C


Sujet :

C#

  1. #1
    Membre à l'essai
    Homme Profil pro
    Architecte réseau
    Inscrit en
    Juin 2011
    Messages
    29
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte réseau
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2011
    Messages : 29
    Points : 15
    Points
    15
    Par défaut P/Invoke sur fonction native C
    Bonjour à tous,

    J'ai un problème qui traine depuis plusieurs jours et c'est pas faute d'avoir essayer tout et n'importe quoi pour remédier à cela mais toujours rien.
    Je m'explique j'ai besoin de faire appel à une fonction C d'un programme extérieur au mien (C#).
    Je me suis donc dis qu'il fallait que je fasse une dll ou j'exporte ma fonction C de telle sorte qu'elle puisse être utilisable sur mon code C# (si il y a un autre moyen je veux bien le connaitre ).
    Le problème c'est que ma fonction C utilise des char * en entrée et sortie.

    Voici comment j'ai exporté ma fonction C :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    extern "C" __declspec(dllexport) char * func(char *pwd)
    Et voici comment j'utilise tout ca en C#
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    [DllImport("func.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] 
    public static extern IntPtr func(StringBuilder pwd); 
     
    ...
     
    StringBuilder pass = new StringBuilder(sclient.pass);
    IntPtr lpStrRet = Marshal.AllocHGlobal(256);
     
    lpStrRet = func(pass);
     
    if (lpStrRet.ToInt32() == 0) throw new Exception("lpStrRet NULL");
    string hashedpwd = Marshal.PtrToStringAuto(lpStrRet);
    Marshal.FreeHGlobal(lpStrRet);
     
    Console.WriteLine("ret func C : " + hashedpwd);
    J'ai eu le droit à une erreur m'indiquant que il ne trouvait pas le point d'entrée, j'ai donc lancé un dumpbin dans une fenetre prompt visual pour vérifier que j'exporte bien ma fonction voici ce que j'ai eu :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    File Type: DLL
     
    Section contains the following exports for func.dll
     
    ..
     
    1 ordinal base
    1 number of functions
    1 number of names
     
    ordinal hint RVA name
     
    1 0 00011113 func = @ILT+270(_func)
    j'ai donc rajouté un paramètre EntryPoint=func dans le dllimport
    et j'ai changé ma fonction C# en _func et la du coup j'ai un message d'erreur qui me dit "L'accès à cet emplacement de la mémoire n'est pas valide. (Exception de HRESULT)".
    Je n'ai donc plus vraiment d'idées sur quoi faire pour remédier à cela si quelqu'un en a une vraiment je suis preneur!

    Merci à tous et bonne journée

  2. #2
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    1 002
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 1 002
    Points : 552
    Points
    552
    Par défaut
    Salut,
    J'ai pas vu d'erreur particulière, peut être que d'autres seront plus attentif que moi.
    Vérifie que ton processus tourne en X86 (32bits) dans les propriétés, tu peux aussi tester avec CharSet = CharSet.Ansi (j'y crois pas mais bon...)

  3. #3
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 314
    Points
    13 314
    Par défaut
    Je vois peut être un problème avec le fait que tu passes un StringBuilder au lieu d'un string.

    Par ailleurs, comme dit au dessus, vérifie bien le coup de 32/64 bits :

    - si ton OS est en 32, pas de soucis.
    - si ton OS est en 64, (ta DLL C doit être en 64 bits ET ton programme en x64 ou AnyCPY) OU (ta DLL C doit être en 32 bits ETton programme C# compilé en x86)

    Par ailleurs, l'usage de la C Calling Conv pour l'appel de fonctions C exportées est plutôt rare. EN C, on exporte en général en convention pascal (stdcall).

    Je ne réponds pas aux questions techniques par MP ! Le forum est là pour ça...


    Une réponse vous a aidé ? utiliser le bouton

    "L’ennui dans ce monde, c’est que les idiots sont sûrs d’eux et les gens sensés pleins de doutes". B. Russel

  4. #4
    Membre à l'essai
    Homme Profil pro
    Architecte réseau
    Inscrit en
    Juin 2011
    Messages
    29
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte réseau
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2011
    Messages : 29
    Points : 15
    Points
    15
    Par défaut
    Bonjour,

    Alors pour ce qui est de l'OS j'ai bien une machine 32bits et j'ai moi même générée la DLL dessus via Visual Studio.
    Pour ce qui est du charSet.Ansi ca ne change absolument rien.
    D'autres part j'ai justement utilisé le StringBuilder car j'ai lu après plusieurs recherches qu'il était nettement plus adapté au string lorsqu'il s'agit de passer une chaîne de caractère en paramètres et que du coté de la DLL on s'attend à recevoir un type char *.
    Par ailleurs je viens d'essayer de changer le type d'export en stdcall, j'ai donc regénérer une dll de la sorte :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    extern "C" 
    	{
    		__declspec(dllexport) char * __stdcall func (char *Password)
    j'ai refais un dumpbin dessus qui me montre que la fonction est bien exportée :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    1 0 0011019 _func @4 = @ILT+20(_func@4)
    ...
    Et malgré tout quand j'essaye de faire l'import coté C# j'ai une exception qui me dit qu'il ne trouve pas le point d'Entrée '_func' alors que je l'ai bien mis en paramètre dans mon [DLLimport ..]
    Voila je comprends pas pourquoi ca ne marche pas si quelqu'un voit quelque chose, ou voit une autre manière de procéder pour utiliser ma fonction C sans passer par une DLL je lui en serais très reconnaissant.

    Merci

  5. #5
    Expert confirmé
    Inscrit en
    Avril 2008
    Messages
    2 564
    Détails du profil
    Informations personnelles :
    Âge : 64

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 564
    Points : 4 441
    Points
    4 441
    Par défaut
    bonjour yachiro
    Bonjour bluuedeep.
    Je pense qu'en fait tes deboires proviennent beaucoup de la meconnaissance de la representation interne des "types" courants (char ,double,int,uint etc...)...et c'est l'erreur commune ("common mistake")...
    La question est toujours la meme?
    Le type X1 (compilo du langage X1) correspond-il bien au type X2 (compilo du langage X2)....
    Cette etape est franchi toujours allegrement sans reflexion prealable.....

    Interface C:
    - un char de C standard est en interne represente comme un "signed byte" (octet avec signe)...
    Doc MSDN
    Storage of Basic Types
    Sizes of Fundamental Types
    Storage char, unsigned char, signed char => 1 byte

    Plus loin .....
    rubrique Data Type Specifiers and Equivalents
    This book generally uses the forms of the type specifiers listed in the following table rather than the long forms, and it assumes that the char type is signed by default
    Cote C# l'animal "approprie" est le type Sbyte car c'est un "signed Byte" mais helas la methode du Marshalleur :
    -MarshalCopy[bytes[] source,int startIindex,Intptr destination,int length)
    refuse un argument de source type SByte...
    Heureusement que C# fait un conversion implicite de Byte en uint(unsigned int qui correspond au char de C) et on peut utiliser un MarshalCopy avec un array de bytes......
    Doc MSDN rubrique :
    Implicit Numeric Conversions Table (C# Reference)
    Le tableau suivant répertorie les conversions numériques implicites prédéfinies.Des conversions implicites peuvent avoir lieu dans de nombreuses situations, notamment lors de l'appel de méthodes et de la définition d'instructions d'assignation
    (voir tableau qui suit):
    De byte En short, ushort, int, uint, long, ulong, float, double ou decimal

    La suite est simple :
    - le string(je prefere) est converti en managed array de char...
    - le managed array de char est converti en array de bytes...
    - une taille est alloue cote memoire non manage() en utilise la formule requestedSize =SizeOfType*LenghtInstance(longueur pour les arrays,structures etc...)....avec Marshal.AllocHGlobal qui nous renvoi un IntPtr pointant sur le debut de memoire allouee...
    - appel à func
    - comme ta func utilise un pointeur comme argument c'est lui qui contiendra directement la valeur renvoye ("ACHIRO" en majuscule)....
    - une boucle lit les octets un à un dans la memoire non manage
    avec Marshal.ReadByte(IntPtr ptr, int offset) ,les convertit en char et les stockes dans managed array (prealablement remis à zero)

    test func C:
    code fichier code .h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    extern "C" __declspec(dllexport)  char* func( char*  pwd);
    code fichier code .cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    // WinFuncC.cpp*: définit les fonctions exportées pour l'application DLL.
     
    #include <ctype.h>
    #include "stdafx.h"
    #include "WinFunc.h"
    #include <string> // program uses C++ standard string class
    using std::string;
     
     
     
    char* func( char*  pwd)
     {
     
    	while (*pwd != ' ' )
    	{
    	   *pwd = toupper( *pwd);   /* convertit pwd  en majuscules */
    		pwd++;
     
    	}
     
    	return pwd;
     }
    code .cs du form utilisateur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
     
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
     
    namespace WinCallingFunc
    {
        public partial class Form2 : Form
        { 
            // simple string 
            string strPass = string.Empty;
            public Form2()
            {
                InitializeComponent();
            }
     
            private void button1_Click(object sender, EventArgs e)
            {
                strPass = "yaichro";
                textBox1.Text = strPass;
     
                /*  managed array of char from string.*/
     
                char[] managedArray = strPass.ToArray();
     
               /*  vu que le C traite le type "char" comme un "unsigned byte"
                 declare un tableau de type Byte  C#(signed)  */ 
     
                /* Le C# convertit implicitement le byte signed en byte unsigned */
                /* le Sbyte(unsigned) helas n'est pas accepte par le Marshalleur Marshal.Copy*/ 
     
                Byte[] arrBytes = new Byte[managedArray.Length];
     
               /* convertit le "managedArray" en array de Byte */
     
                for (int i = 0; i < managedArray.Length; i++)
                {
                    arrBytes[i] = Convert.ToByte(managedArray[i]);
     
                }
     
                /* alloue taille memoire = taille du type*longueur intanstance(s'il en est) */
                IntPtr lpStrRet = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Byte))*arrBytes.Length );
     
                /* copie les bytes en memoire non managee */ 
     
                Marshal.Copy (  arrBytes, 0, lpStrRet, arrBytes.Length);
                IntPtr lpStrRet2 = func(lpStrRet);
     
                /*  reset managedArray */
     
                managedArray = new char[managedArray.Length];
     
                /*boucle en memoire non managee et lit byte par byte */ 
     
                for (int j = 0; j < managedArray.Length; j++)
                {
                    arrBytes[j] = Marshal.ReadByte(lpStrRet, j);
                    managedArray[j] = Convert.ToChar(arrBytes[j]);
                }
     
                /*  libere memoire non managee */ 
     
                Marshal.FreeHGlobal(lpStrRet);
     
     
                /* affiche resultat dans textbox */
     
                StringBuilder sb = new StringBuilder();
                sb.Append(managedArray);
                textBox2.Text = sb.ToString(); 
            }
            [DllImport("WinFuncC.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
            public static extern IntPtr func( IntPtr ptr);
        }
    }
    Le seul inconvenient des methodes qui passent un ptr sur array c'est que au retour il faut faire attention dans l'acces au resultat à se limiter à la taille marshalle car au dela on ignore ce qu'il y a comme donne surtout si la function accede n'est pas de notre cru.....

    bon marshalling............

  6. #6
    Membre à l'essai
    Homme Profil pro
    Architecte réseau
    Inscrit en
    Juin 2011
    Messages
    29
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte réseau
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2011
    Messages : 29
    Points : 15
    Points
    15
    Par défaut
    Bonjour,

    Effectivement tu as raison ceci est du à ma méconnaissance des types!
    Je te remercie sincèrement pour cette réponse très claire et détaillée vraiment!
    J'essaie ca!!

  7. #7
    Expert confirmé
    Inscrit en
    Avril 2008
    Messages
    2 564
    Détails du profil
    Informations personnelles :
    Âge : 64

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 564
    Points : 4 441
    Points
    4 441
    Par défaut
    rebonjour yachiro

    Relativement à la valeur de chaine renvoye par ton func ,dans le cas ou elle est superiure à la longueur de chaine transmise ,il faut se faire allouer un bloc de memoire non manage egal à celui de la chaine à recevoir
    Ceci entraine:
    -de declarer un arrBytes[127]
    127 parce que char en C c'est du code ascii et il y a au max 127 .....
    -de copier les 1er caracteres de managedArray dans l'arrayBytes......
    -allouer le bloc memoire Marshal.AllocHGlobal
    - le reste comme deja vu dans le code poste precdemment.....

    voici une version revue "func127" qui recoit la chaine "achiro" et qui renvoie les caracteres Ascii(1-126) (j'evite le 0 car c'est le terminateur null de chaine,si on l'inclut le func sort de la boucle prematurement et nous renverra une chaine vide).....
    Remarque egalement comment en C le char n'est qu'un simple int.....
    code .c du function :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    /* fichier .h */
    extern "C" __declspec(dllexport)  char* func127( char*  pwd);
     
    /* fichier .cpp */
     char* func127(char*  pwd)
     { 
       	int i = 1;
     
    	/* recupere les 127 caracteres ascii */
    	while (  i < 128)
        { 
    			*pwd = i;
    			 pwd++;
    			 i++;
    	}
     
    	 return pwd;
     }
    code behind .cs du winformles modifs sont signalees en commentaires)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
     
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Globalization;
     
    namespace WinCallingFunc
    {
        public partial class Form2 : Form
        { 
            // simple string 
            string strPass = string.Empty;
            public Form2()
            {
                InitializeComponent();
            }
     
            private void button2_Click(object sender, EventArgs e)
            {
                strPass = "yaichro";
                textBox1.Text = strPass;
                char[] managedArray = strPass.ToArray();
     
                /* MODIF:ON DECLARE UN arrBytes AJUSTE A 127 */
                Byte[] arrBytes = new Byte[127];
     
                /* MODIF:RECOPIE DU managedArray DEDANS */
                for (int i = 0; i < managedArray.Length; i++)
                {
                    arrBytes[i] = Convert.ToByte(managedArray[i]);
     
                }
     
                IntPtr lpStrRet = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Byte)) *arrBytes.Length);
                Marshal.Copy(arrBytes, 0, lpStrRet, arrBytes.Length);
                IntPtr lpStrRet2 = func127(lpStrRet);
     
                /* MODIF: ICI RESET & REDIMENSIONNER  à taille 127*/
                managedArray = new char[arrBytes.Length];
     
                for (int j = 0; j < managedArray.Length; j++)
                {
                    arrBytes[j] = Marshal.ReadByte(lpStrRet, j);
                    managedArray[j] = Convert.ToChar(arrBytes[j]);
     
                }
     
                Marshal.FreeHGlobal(lpStrRet);
     
                StringBuilder sb = new StringBuilder();
                sb.Append(managedArray);
                textBox2.Text = sb.ToString();
            }
            [DllImport("WinFuncC.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
            public static extern IntPtr func127(IntPtr ptr); 
     
        }
    }
    Le code qui recupere le managedArray peut etre optimise (en eliminant la boucle) et tenir compte des encodages (ansi,unicode,auto etc):
    voici la boucle que tu peux remplacer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    // BOUCLE A REMPLACER
                //for (int j = 0; j < managedArray.Length; j++)
                //{
                //    arrBytes[j] = Marshal.ReadByte(lpStrRet, j);
                //    managedArray[j] = Convert.ToChar(arrBytes[j]);
     
                //}
     
                /* par cette version Ansi(ascii) */
                strPass = Marshal.PtrToStringAnsi(lpStrRet, arrBytes.Length);
                managedArray = strPass.ToArray();
                /* ou bien  par cette cette version Unicode */
                strPass = Marshal.PtrToStringUni(lpStrRet, arrBytes.Length);
                managedArray = strPass.ToArray();
    bonne code..............

  8. #8
    Membre à l'essai
    Homme Profil pro
    Architecte réseau
    Inscrit en
    Juin 2011
    Messages
    29
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte réseau
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2011
    Messages : 29
    Points : 15
    Points
    15
    Par défaut
    Bonjour,

    J'ai fais ce que tu m'as conseillé et ca marche niquel enfin!!

    Je te remercie sincèrement car tu m'auras énormément aidé!!
    Merci beaucoup aux autres également à bientôt!!

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème complétion sur les fonctions natives PHP
    Par syber dans le forum Eclipse PHP
    Réponses: 1
    Dernier message: 05/10/2009, 14h21
  2. Réponses: 5
    Dernier message: 12/01/2005, 20h58
  3. pointeurs sur fonction en C++
    Par cemoi dans le forum C++
    Réponses: 7
    Dernier message: 29/11/2004, 13h19
  4. [langage] Pointeur sur fonction
    Par Fanch.g dans le forum Langage
    Réponses: 2
    Dernier message: 02/10/2004, 10h43
  5. Declaration de fonction retournant un pointeur sur fonction
    Par pseudokifaitladifférence dans le forum C
    Réponses: 5
    Dernier message: 11/08/2003, 19h37

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo