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

9 réponses

1 2
Avatar
Patrick Philippot
Hervé HERRY wrote:
J'avoue que je ne sais pas trop pour quelle solution
opter car je n'ai pas encore tout bien assimilé...



Vous n'avez aucun contrôle sur cette DLL? La meilleure solution, c'est
qu'elle soit compilée en Unicode. Elle vient d'où?

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Hervé HERRY
> Vous n'avez aucun contrôle sur cette DLL?



J'allais oublier de répondre à la question de l'Unicode.
Mon cas est un peu particulier car cette DLL, plutôt conséquente, a été à
l'origine codée pour PC. Comme je fais la soeur jumelle de l'application qui
exploite cette DLL, mais pour Pocket PC, j'ai repris le code de la DLL et,
moyennant quelques modification mineures à l'aide de directives
préprocesseur, je l'ai recompilée pour Pocket PC (sous eMbedded VC++ 4). Le
problème est de conserver le maximum de code en commun -pour les deux
plateformes- afin de faciliter les futures évolutions de la DLL.
Donc, elle n'a pas été conçue pour gérer l'Unicode et dans la mesure du
possible -et aussi parce que je ne suis pas l'auteur de cette DLL- je
voudrais ne pas la modifier...

Est-ce que l'on pourrait voir la déclaration de la fonction en question


dans le .H?

Biensûr :
int __stdcall CORUS_OpenSerialPort(char *com, int *id_iflag, BOOL
m_support_modem, BOOL manage_trace, BOOL datascope,BOOL ontop);

"Patrick Philippot" a écrit dans le
message de news:
Hervé HERRY wrote:
> J'avoue que je ne sais pas trop pour quelle solution
> opter car je n'ai pas encore tout bien assimilé...

Vous n'avez aucun contrôle sur cette DLL? La meilleure solution, c'est
qu'elle soit compilée en Unicode. Elle vient d'où?

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




Avatar
Patrick Philippot
Hervé HERRY wrote:
J'ai repris le code de la DLL et, moyennant quelques
modification mineures à l'aide de directives préprocesseur,
je l'ai recompilée pour Pocket PC ...Le problème est de
conserver le maximum de code en commun



Voilà plus de 10 ans que je prêche la même chose concernant Unicode
:-))) .

L'écriture d'un code compilé en ASCII et "migrable" vers Unicode ne
représente pas un effort particulier. On utilise TCHAR.H au lieu de
string.h, on utilise les fonctions _tcsxxx au lieu des fonctions strxxx,
on ne confond pas char et octet, on fait attention à la taille des
buffers (sizeof (TCHAR)) et c'est tout.

Ensuite on #define UNICODE ou pas selon que l'on veut de l'Ascii ou de
l'Unicode. C'est incroyablement simple.

Allez tenez, je suis généreux aujourd'hui, voilà un extrait d'un de mes
cours sur ce sujet:

----------------------------------

(tous droits de reproduction réservés à MainSoft - www.mainsoft.fr)

Coder UNICODE

La première constatation du développeur mettant en oeuvre UNICODE est qu'il
va être nécessaire de travailler avec des buffers dont la taille sera
double des buffers habituels (les char étant codés sur un seul octet).
Dans certains cas extrêmes, cela peut poser quelques problèmes (de pile
par exemple).

Le type standard représentant les caractères en UNICODE est wchar_t.

Pour chaque fonction strxxxx de la bibliothèque standard C, on va donc
trouver un équivalent UNICODE. Par exemple:

char* strcat(char*, const char*);

va devenir

wchar_t* wcscat(wchar_t*, const wchar_t*);

On notera donc que toute librairie développée à partir de ces fonctions
de base de la C Runtime Library est à revoir (ou au minimum à recompiler
mais bien souvent cela ne suffira pas).

Il est bien évident que l'on a besoin ici d'une interface un peu plus
transparente qui s'adaptera automatiquement selon que l'on décide de
supporter UNICODE ou non. Les fichiers .H de Win32 mettent en oeuvre un
jeu de macros adapté. Pour rendre son code le plus possible indépendant
du choix UNICODE ou non, on procédera comme suit:

· Au lieu d'inclure STRING.H on inclura TCHAR.H.
· Au lieu d'utiliser char ou wchar_t, on utilisera le type TCHAR.
· Tout codage de texte " en dur " dans le code (à éviter), sera
fait par le biais de la macro __TEXT ou _T (identiques). Exemple:

TCHAR* szText = _T("Mon_Texte");

· On utilisera des types normalisés comme LPTSTR et LPCTSTR au lieu
de type spécifiques comme char* et const char*.
· On évitera les confusions entre tableaux de caractères et
tableaux d'octets.
· Au lieu d'utiliser des noms de fonctions standard, on utilisera
les alias définis dans TCHAR.H. Par exemple, _tcscpy deviendra strcpy si
_UNICODE n'est pas défini et wcscpy dans le cas contraire.
· Si l'on désire supporter UNICODE on fera un #define de _UNICODE.

On se rend compte dans la pratique que ces changements ne sont pas très
complexes. Le seul vrai problème est le code dérivé des fonctions
standard et encapsulé dans des classes ou des bibliothèques. La classe
CString des MFC supporte les 2 alternatives.

Certaines fonctions de l'API Win32 ont des prototypes différents selon
que UNICODE est actif ou non. De cette manière, NT sait toujours s'il
doit effectuer une conversion ou pas. Ces différences n'apparaissent pas
au développeur s'il applique les règles citées plus haut. Mises à part
quelques fonctions de la RTL C qui ne peuvent pas être converties et ont
donc des équivalents en Win32 (par ex. tolower et toupper), le passage à
un support transparent d'UNICODE se fera sans difficultés.

En cas de besoin on pourra toujours convertir des chaînes ANSI en
UNICODE et inversement à l'aide de MultiByteToWideChar et
WideCharToMultiByte. Attention aux tailles des buffers!!!

Ressources

Une remarque finale concernant les ressources. Toute chaîne de
caractères déclarée dans vos ressources est automatiquement et
obligatoirement transformée par le compilateur de ressources en chaîne
UNICODE. Selon que vous définissez _UNICODE ou non, la fonction
LoadString sera convertie dans le prototype adéquat (LoadStringW ou
LoadStringW - la fonction LoadString n'existant pas en réalité, c'est
simplement une macro).

----------------------------------

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Patrick Philippot
Voilà le code généré par cet outil pour une utilisation de la fonction
depuis le Compact Framework:

[DllImport("Corus.dll", Charset=Charset.Unicode)]
public static extern int CORUS_OpenSerialPort (string com, ref int
id_iflag, int m_support_modem, int manage_trace, int datascope, int
ontop);

Connaissant la réputation de Paul Yao, ça devrait être ok.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Patrick Philippot
Patrick Philippot wrote:
[DllImport("Corus.dll", Charset=Charset.Unicode)]
public static extern int CORUS_OpenSerialPort (string com, ref int
id_iflag, int m_support_modem, int manage_trace, int datascope, int
ontop);

Connaissant la réputation de Paul Yao, ça devrait être ok.



Non, ce n'est pas correct. Charset.Unicode n'est pas le bon choix.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Hervé HERRY
Comme Pocket PC fonctionne toujours en Unicode, son utilitaire met peut-être
cet attribut par défaut..

"Patrick Philippot" a écrit dans le
message de news:%
Patrick Philippot wrote:
> [DllImport("Corus.dll", Charset=Charset.Unicode)]
> public static extern int CORUS_OpenSerialPort (string com, ref int
> id_iflag, int m_support_modem, int manage_trace, int datascope, int
> ontop);
>
> Connaissant la réputation de Paul Yao, ça devrait être ok.

Non, ce n'est pas correct. Charset.Unicode n'est pas le bon choix.

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




Avatar
Patrick Philippot
Hervé HERRY wrote:
Comme Pocket PC fonctionne toujours en Unicode, son utilitaire met
peut-être cet attribut par défaut.



Je lui ai posé la question (Paul est un collègue MVP).

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Hervé HERRY
C'est très gentil à vous de m'avoir envoyé votre cours, je le garde sous le
coude. :-)) Cependant, je ne peux malheureusement pas consacrer le temps
nécessaire pour convertir toute ou partie de cette DLL...

En attendant, j'ai pu essayer la fonction de conversion
Encoding.ASCII.GetBytes() que vous m'avez indiquée et cela semble
fonctionner à merveille ! :-o Heureusement, mais vous l'aviez certainement
déjà vérifié, toutes les méthodes employées sont supportées par la Compact
Framework. Cela me permet de saisir les quelques chaînes de caractères dans
mon code sans craindre de faire des erreurs.

Je suis allé visiter votre site, très bien (et classe :-)) soit dit en
passant, ce qui m'a permis d'apprendre ce que vous faites, "développement
et assistance technique, spécialisé dans les technologies Microsoft" [pour
les personnes qui suivraient notre discussion]. Je ne suis pas encore très
habitué aux forums, mais je voulais vous dire que j'apprécie beaucoup le
soutien que vous m'apportez bénévolement. Voilà, merci.

"Patrick Philippot" a écrit dans le
message de news:
Hervé HERRY wrote:
> J'ai repris le code de la DLL et, moyennant quelques
> modification mineures à l'aide de directives préprocesseur,
> je l'ai recompilée pour Pocket PC ...Le problème est de
> conserver le maximum de code en commun

Voilà plus de 10 ans que je prêche la même chose concernant Unicode
:-))) .

L'écriture d'un code compilé en ASCII et "migrable" vers Unicode ne
représente pas un effort particulier. On utilise TCHAR.H au lieu de
string.h, on utilise les fonctions _tcsxxx au lieu des fonctions strxxx,
on ne confond pas char et octet, on fait attention à la taille des
buffers (sizeof (TCHAR)) et c'est tout.

Ensuite on #define UNICODE ou pas selon que l'on veut de l'Ascii ou de
l'Unicode. C'est incroyablement simple.

Allez tenez, je suis généreux aujourd'hui, voilà un extrait d'un de mes
cours sur ce sujet:

----------------------------------

(tous droits de reproduction réservés à MainSoft - www.mainsoft.fr)

Coder UNICODE

La première constatation du développeur mettant en oeuvre UNICODE est


qu'il
va être nécessaire de travailler avec des buffers dont la taille sera
double des buffers habituels (les char étant codés sur un seul octet).
Dans certains cas extrêmes, cela peut poser quelques problèmes (de pile
par exemple).

Le type standard représentant les caractères en UNICODE est wchar_t.

Pour chaque fonction strxxxx de la bibliothèque standard C, on va donc
trouver un équivalent UNICODE. Par exemple:

char* strcat(char*, const char*);

va devenir

wchar_t* wcscat(wchar_t*, const wchar_t*);

On notera donc que toute librairie développée à partir de ces fonctions
de base de la C Runtime Library est à revoir (ou au minimum à recompiler
mais bien souvent cela ne suffira pas).

Il est bien évident que l'on a besoin ici d'une interface un peu plus
transparente qui s'adaptera automatiquement selon que l'on décide de
supporter UNICODE ou non. Les fichiers .H de Win32 mettent en oeuvre un
jeu de macros adapté. Pour rendre son code le plus possible indépendant
du choix UNICODE ou non, on procédera comme suit:

· Au lieu d'inclure STRING.H on inclura TCHAR.H.
· Au lieu d'utiliser char ou wchar_t, on utilisera le type TCHAR.
· Tout codage de texte " en dur " dans le code (à éviter), sera
fait par le biais de la macro __TEXT ou _T (identiques). Exemple:

TCHAR* szText = _T("Mon_Texte");

· On utilisera des types normalisés comme LPTSTR et LPCTSTR au lieu
de type spécifiques comme char* et const char*.
· On évitera les confusions entre tableaux de caractères et
tableaux d'octets.
· Au lieu d'utiliser des noms de fonctions standard, on utilisera
les alias définis dans TCHAR.H. Par exemple, _tcscpy deviendra strcpy si
_UNICODE n'est pas défini et wcscpy dans le cas contraire.
· Si l'on désire supporter UNICODE on fera un #define de _UNICODE.

On se rend compte dans la pratique que ces changements ne sont pas très
complexes. Le seul vrai problème est le code dérivé des fonctions
standard et encapsulé dans des classes ou des bibliothèques. La classe
CString des MFC supporte les 2 alternatives.

Certaines fonctions de l'API Win32 ont des prototypes différents selon
que UNICODE est actif ou non. De cette manière, NT sait toujours s'il
doit effectuer une conversion ou pas. Ces différences n'apparaissent pas
au développeur s'il applique les règles citées plus haut. Mises à part
quelques fonctions de la RTL C qui ne peuvent pas être converties et ont
donc des équivalents en Win32 (par ex. tolower et toupper), le passage à
un support transparent d'UNICODE se fera sans difficultés.

En cas de besoin on pourra toujours convertir des chaînes ANSI en
UNICODE et inversement à l'aide de MultiByteToWideChar et
WideCharToMultiByte. Attention aux tailles des buffers!!!

Ressources

Une remarque finale concernant les ressources. Toute chaîne de
caractères déclarée dans vos ressources est automatiquement et
obligatoirement transformée par le compilateur de ressources en chaîne
UNICODE. Selon que vous définissez _UNICODE ou non, la fonction
LoadString sera convertie dans le prototype adéquat (LoadStringW ou
LoadStringW - la fonction LoadString n'existant pas en réalité, c'est
simplement une macro).

----------------------------------

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




Avatar
Patrick Philippot
Hervé HERRY wrote:
Je ne suis pas encore très habitué aux forums, mais je
voulais vous dire que j'apprécie beaucoup le soutien
que vous m'apportez bénévolement. Voilà, merci.



Pas de quoi :-). Répondre aux questions posées ici est aussi un moyen de
se former ou de s'entretenir. Tout le monde y gagne. Il y a beaucoup de
bonnes volontés ici et le programme MVP a encouragé les volontaires à
être encore plus présents. Ce qui ne retire rien au travail de support
des autres participants à ces communautés, soit dit en passant (de
toutes façons, le statut de MVP est éphémère et remis en question
régulièrement).

Je vous encourage à étudier les bénéfices de ce programme pour
l'utilisateur et à solliciter ceux des MVPs qui peuvent vous aider dans
vos projets:

http://mvp.support.microsoft.com/default.aspx?LN=FR&x"&y

Ce programme est aussi un des moyens permettant aux utilisateurs de
faire remonter indirectement leurs remarques et réflexions à Microsoft.

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