Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Passage d'un char* en argument d'une fonction de DLL native

19 réponses
Avatar
Hervé HERRY
Bonjour,

J'utilise des fonctions d'une DLL native que j'exploite par P/Invoke avec
[DllImport( "monfichier.dll" )].
Je dois passer un char* contenant par exemple "COM1" en argument d'une
fonction. Mais le char* n'existant plus en C#, je ne sais pas quoi utiliser.

Je précise que j'ai essayé le 'char[]' (mais ça échoue, peut-être du fait
qu'un char en C# est codé sur 16 bits), j'ai essayé le 'ref char', mais je
ne peux stocker qu'un caractère sinon le compilo me jette, j'ai bien
évidemment essayé le string, et la seule bidouille pour que ça fonctionne
c'est avec le byte, codé sur 8 bits cette fois :

byte[] Com = { 67, 79, 77, 49 }; // <=> { 'C', 'O', 'M', '1' }

Si quelqu'un peut me dire ce à quoi je n'ai pas pensé ou bien me donner une
fonction de conversion d'un char 16 bits en char 8 bits, je pense que ça
résoudrait le problème...

Merci d'avance.

--
Hervé HERRY
Ingénieur R&D
ACTARIS metering systems

10 réponses

1 2
Avatar
Patrick Philippot
Hervé HERRY wrote:
J'utilise des fonctions d'une DLL native que j'exploite par P/Invoke
avec [DllImport( "monfichier.dll" )].
Je dois passer un char* contenant par exemple "COM1" en argument d'une
fonction. Mais le char* n'existant plus en C#, je ne sais pas quoi
utiliser.



Bonjour,

Supposons que votre fonction a la signature suivante:

void MaFonc(char * S);

[DllImport("monfichier.dll")]
public static extern void MaFonc(string S);

devrait suffire. La conversion est automatique.

Pour être plus orthodoxe / explicite:

[DllImport("monfichier.dll")]
public static extern void MaFonc([MarshalAs(UnmanagedType.LPStr)] string
S);

mais c'est a priori inutile.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Gwenn Brogniart
Hervé HERRY wrote:
Bonjour,

J'utilise des fonctions d'une DLL native que j'exploite par P/Invoke avec
[DllImport( "monfichier.dll" )].
Je dois passer un char* contenant par exemple "COM1" en argument d'une
fonction. Mais le char* n'existant plus en C#, je ne sais pas quoi utiliser.

Je précise que j'ai essayé le 'char[]' (mais ça échoue, peut-être du fait
qu'un char en C# est codé sur 16 bits), j'ai essayé le 'ref char', mais je
ne peux stocker qu'un caractère sinon le compilo me jette, j'ai bien
évidemment essayé le string, et la seule bidouille pour que ça fonctionne
c'est avec le byte, codé sur 8 bits cette fois :

byte[] Com = { 67, 79, 77, 49 }; // <=> { 'C', 'O', 'M', '1' }

Si quelqu'un peut me dire ce à quoi je n'ai pas pensé ou bien me donner une
fonction de conversion d'un char 16 bits en char 8 bits, je pense que ça
résoudrait le problème...

Merci d'avance.




Le namespace System.Runtime.InteropServices contient de multiples
services pour interagir avec le code non managé ou avec des objets COM.
Notamment la fonction Marshal.StringToHGlobalAnsi(string) dans votre cas.

<CODE>
using System.Runtime.InteropServices;

[...]

string s = "COM1";
IntPtr AnsiString = Marshal.StringToHGlobalAnsi(s);
FonctionNonManagee(AnsiString);
Marshal.FreeHGlobal(AnsiString); // voir ci-après
</CODE>

Remarque : Comme spécifié dans la documentation il ne faut pas oublier
d'appeler FreeHGlobal quand AnsiString n'est plus utile car la zone
mémoire allouée par StringToHGlobalAnsi est non-managée, donc doit être
libérée explicitement.

--
Gwenn
Avatar
Gwenn Brogniart
Comme vient de l'expliquer Patrick Philippot, il y a effectivement le
Marshaling automatique et l'attribut MarshalAs.
Quand on peut il faut l'utiliser. C'est plus simple et plus élégant que
de le faire à la main.

--
Gwenn
Avatar
Hervé HERRY
Tout d'abord, merci à vous, Patrick et Gwenn, d'avoir pris le temps de me
répondre. :-)

J'ai donc essayé toute vos solutions en vain. Mais il est à préciser que ce
programme s'exécute sur un Pocket PC. J'avais oublié de le dire et cela
change peut-être un peu la donne... :-|

- 1er essai :

[ DllImport( "Corus.dll" )] public static extern int CORUS_OpenSerialPort(
string com, ref int id_flag, bool support_modem, bool manage_trace, bool
datascope, bool ontop );
...
string cCom = "COM1";
CORUS_OpenSerialPort( cCom, ref ID_Flag, false, false, false, false );
-> Pas d'exception, mais ma fonction ne trouve apparemment pas ce qu'elle
cherche (je n'ai pas encore pris le temps d'approfondir)

- 2ème essai :
[ DllImport( "Corus.dll" )]
public static extern int CORUS_OpenSerialPort(
[MarshalAs(UnmanagedType.LPStr)] string com, ref int id_flag, bool
support_modem, bool manage_trace, bool datascope, bool ontop );
...
string cCom = "COM1";
CORUS_OpenSerialPort( cCom, ref ID_Flag, false, false, false, false );
-> Erreur à la compilation "Le type ou le nom d'espace de noms 'MarshalAs'
est introuvable (une directive using ou une référence d'assembly est-elle
manquante ?)" -> C'est peut-être dû aux fonctions limitées du Compact
Framework car j'ai pourtant mis "using System.RuntimeInteropServices;" (à
moins qu'il n'y ait autre chose à déclarer ?...)

- 3ème et dernier essai, celui de Gwenn :
[ DllImport( "Corus.dll" )] public static extern int CORUS_OpenSerialPort(
IntPtr com, ref int id_flag, bool support_modem, bool manage_trace, bool
datascope, bool ontop );
...
string cCom = "COM1";
IntPtr AnsiString = Marshal.StringToHGlobalAnsi(cCom);
int i;
i = CORUS_OpenSerialPort( AnsiString, ref ID_Flag, false, false, false,
false );
Marshal.FreeHGlobal(AnsiString);
return i;
-> Le compilateur ne trouve ni StringToHGlobalAnsi(), ni FreeHGlobal() dans
la classe Marshal... certainement à cause des fonctions allégées du Compact
Framework...

Je vais fouiller un peu pour savoir ce qu'il reçoit dans le 1er cas et
pourquoi ça ne lui convient pas.
Si vous avez d'autres idées en attendant, je suis preneur...

Encore merci.

--
Hervé HERRY
Ingénieur R&D
ACTARIS metering systems
Avatar
Patrick Philippot
Hervé HERRY wrote:
J'ai donc essayé toute vos solutions en vain. Mais il est à préciser
que ce programme s'exécute sur un Pocket PC. J'avais oublié de le
dire et cela change peut-être un peu la donne... :-|



A priori, c'est pareil.

Il n'y aurait pas un problème de convention d'appel? Convention C au
lieu de stdcall ou quelque chose de ce genre? Auquel cas, il faudrait
préciser la convention utilisée dans l'attribut DllImport.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Hervé HERRY
Je vous confirme que la fonction native est bien paramatrée en __stdcall.
Mais je continue à croire que le Compact Framework peut-être à l'origine des
erreurs de compilation obtenues avec les essais 2 et 3. Par exemple, j'ai
voulu essayer de préciser CharSet=Ansi dans le DllImport, or il refuse et si
on consulte la MSDN Library, c'est indiqué que seuls les paramètres Auto et
Unicode sont pris en charge... De plus, si on regarde les membres de la
classe Marshal, les méthodes que m'a proposées Gwenn ne sont pas prises en
charge par le CF...

Toujours merci, et espérant trouver une solution avec votre aide...

H. HERRY

"Patrick Philippot" a écrit dans le
message de news:
Hervé HERRY wrote:
> J'ai donc essayé toute vos solutions en vain. Mais il est à préciser
> que ce programme s'exécute sur un Pocket PC. J'avais oublié de le
> dire et cela change peut-être un peu la donne... :-|

A priori, c'est pareil.

Il n'y aurait pas un problème de convention d'appel? Convention C au
lieu de stdcall ou quelque chose de ce genre? Auquel cas, il faudrait
préciser la convention utilisée dans l'attribut DllImport.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr




Avatar
Patrick Philippot
Hervé HERRY wrote:
Je vous confirme que la fonction native est bien paramatrée en
__stdcall. Mais je continue à croire que le Compact Framework
peut-être à l'origine des erreurs de compilation obtenues avec les
essais 2 et 3. Par exemple, j'ai voulu essayer de préciser
CharSet=Ansi dans le DllImport, or il refuse et si on consulte la
MSDN Library, c'est indiqué que seuls les paramètres Auto et Unicode
sont pris en charge... De plus, si on regarde les membres de la
classe Marshal, les méthodes que m'a proposées Gwenn ne sont pas
prises en charge par le CF...



C'est peut-être ça, en effet. Voir
http://www.c-sharpcorner.com/Code/2004/June/DevelopCFApps.asp et
particulièrement ce passage:

"
6. MarshalAS attribute supported by .Net Framework is not available in
.Net Compact Framework. So type conversion need to be ensured in the
managed code itself.
"

Essayez voir de remplacer string par byte[] dans le DllImport et de
procéder comme suit:

byte[] buf = Encoding.ASCII.GetBytes(MaChaine);
MaFonction(buf);

Autre possibilité: passez un StringBuilder au lieu d'une String mais a
priori, ça ne va pas fonctionner.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Patrick Philippot
Je viens de trouver ça:

http://www.paulyao.com/resources/tools/pinvoke.asp

Cela vaudrait le coup d'essayer. Un outil qui semble bien intéressant...

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Patrick Philippot
Hervé HERRY wrote:
Je vous confirme que la fonction native est bien paramatrée en
__stdcall.



Il y a une chose qui m'interpelle: s'il s'agit d'une DLL développée pour
Windows CE, pourquoi n'est-elle pas compilée directement en Unicode?
Est-ce que l'on pourrait voir la déclaration de la fonction en question
dans le .H? N'y aurait-il pas un #define UNICODE / _UNICODE dans le code
de cette DLL?

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Hervé HERRY
Je viens juste de trouver vos 3 messages. J'étais en train de faire des
tests basiques de passage d'arguments. Ce qui est sûr c'est qu'avec le
byte[], ça semble fonctionner. Le plus embêtant est de le mettre en forme
(cf. mon message d'origine). Mais avec la fonction de conversion que vous
m'avez communiquée (Encoding.ASCII.GetBytes( )), je vais peut-être arriver à
m'en sortir !

Je planche aussi sur le passage de pointeurs (par exemple int*)... J'ai
d'abord utilisé le mot clé "ref" dans l'appel de ma fonction, puis j'ai lu
dans le livre "Pratique de .NET et C#" de Patrick Smacchia qu'il fallait
passer des pointeurs "fixed" pour être sûr que le garbage collector ne
déplace pas un objet en mémoire pendant qu'il est utilisé par du code non
géré... J'avoue que je ne sais pas trop pour quelle solution opter car je
n'ai pas encore tout bien assimilé...

Votre aide est précieuse, toujours merci.

Hervé HERRY.

"Patrick Philippot" a écrit dans le
message de news:
Hervé HERRY wrote:
> Je vous confirme que la fonction native est bien paramatrée en
> __stdcall.

Il y a une chose qui m'interpelle: s'il s'agit d'une DLL développée pour
Windows CE, pourquoi n'est-elle pas compilée directement en Unicode?
Est-ce que l'on pourrait voir la déclaration de la fonction en question
dans le .H? N'y aurait-il pas un #define UNICODE / _UNICODE dans le code
de cette DLL?

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr




1 2