FAQ C++/CLI et VC++.Net
FAQ C++/CLI et VC++.NetConsultez toutes les FAQ
Nombre d'auteurs : 29, nombre de questions : 248, création le 22 février 2013
- Peut-on utiliser le Compact Framework avec le C++/CLI ?
- Comment calculer la différence de temps entre deux dates ?
- Comment mesurer un intervalle de temps avec précision ?
- Comment modifier le texte de la barre de titre (fenêtre et console)?
- Comment utiliser le cryptage avec l'algorithme Rijndael ?
- Comment travailler avec les fichiers de configuration ?
- Comment lire une valeur dans un fichier de configuration ?
- Comment écrire une valeur dans un fichier de configuration ?
- Comment crypter un fichier de configuration automatiquement en RSA ?
- Comment retirer les accents d'une chaîne de caractères ?
Cette définition est tirée de MSDN
Le .NET Compact Framework est un environnement indépendant du matériel permettant d'exécuter des programmes sur divers périphériques informatiques à ressources limitées : assistants numériques personnels (PDA, Personal Data Assistant) tels que le Pocket PC, téléphones mobiles, décodeurs, périphériques informatiques automobiles et périphériques personnalisés intégrés au système d'exploitation Windows CE .NET.
Le .NET Compact Framework est un sous-ensemble de la bibliothèque de classes du .NET Framework, mais contient également des classes spécialement conçues à son intention. Il hérite la totalité de l'architecture de Common Language Runtime et d'exécution de code managé du .NET Framework.
Malheureusement, à l'heure actuelle, il n'est pas possible d'utiliser le C++/CLI pour utiliser le Compact Framework. Les mécanismes d'intéropérabilités (It Just Works) n'étant pas disponibles dans le CF, il a été jugé inutile de permettre l'utilisation du C++/CLI, mettant en avant plutôt l'utilisation de C#.
Mais il n'est pas totalement exclu que les équipes VC++ et CF se mettent à travailler ensemble dans l'avenir, même si cela parait peu probable.
Lien : It Just Works
Grâce à la redéfinition de l'opérateur - (mais aussi de +, ==, !=, >, >=, <, <=) avec la classe TimeSpan, il est possible de faire la différence entre 2 objets DateTime
Calculons le nombre de jours écoulés depuis la création de cette question :
DateTime DateCourante =
DateTime::
Now;
DateTime DateCreationquestion =
DateTime(2007
, 1
, 3
);
TimeSpan ^
Ts =
DateCourante -
DateCreationquestion;
Console::
WriteLine("Il s'est écoulé {0} jour(s) depuis la création de cette question !"
, Ts->
Days);
Avec la version 2 du Framework, il est apparu la classe StopWatch qui permet de mesurer un intervalle de temps avec grande précision :
Stopwatch ^
maMesureDeTemps =
gcnew Stopwatch();
// Démarrage de l'intervalle de temps
maMesureDeTemps->
Start();
// ...
// Fin de l'intervalle de temps
maMesureDeTemps->
Stop();
Console::
WriteLine("L'exécution du code a pris : {0}"
, maMesureDeTemps->
Elapsed.ToString());
Lien : Voir l'article de Webman
Lien : System.Diagnostics.StopWatch
Lien : Voir aussi avec l'API Win32
Pour une application Windows, c'est la propriété Text de la la classe Form qui contient le texte de la barre de titre :
this
->
Text =
"Mon titre"
;
Pour ce qui est d'une application console, il faut passer par la classe Console avec la propriété Title :
Console::
Title =
"Mon titre"
;
Le framework.net fourni tout une série de classe pour faciliter le cryptage / décryptage de données.
Voici un exemple de génération de clé à partir d'une chaîne, ainsi que le cryptage et décryptage de fichiers.
Notez les méthodes pour crypter/décrypter des chaînes, ainsi que l'utilisation de BinaryReader et BinaryWritter pour crypter/décrypter un fichier.
using
namespace
System;
using
namespace
System::
Text;
using
namespace
System::Security::
Cryptography;
using
namespace
System::
IO;
void
GenerateKey(String ^
SecretPhrase, array<
unsigned
char
>
^&
Key, array<
unsigned
char
>
^&
IV)
{
array<
unsigned
char
>
^
bytePhrase =
Encoding::
ASCII->
GetBytes(SecretPhrase);
SHA384Managed ^
sha384 =
gcnew SHA384Managed();
sha384->
ComputeHash(bytePhrase);
array<
unsigned
char
>
^
result =
sha384->
Hash;
for
(int
loop =
0
; loop <
24
; loop++
)
Key[loop] =
result[loop];
for
(int
loop =
24
; loop <
40
; loop++
)
IV[loop -
24
] =
result[loop];
}
array<
unsigned
char
>
^
Crypter(array<
unsigned
char
>
^
encrypted, String ^
keyPhrase)
{
array<
unsigned
char
>
^
Key =
gcnew array<
unsigned
char
>
(24
);
array<
unsigned
char
>
^
IV =
gcnew array<
unsigned
char
>
(16
);
GenerateKey(keyPhrase, Key, IV);
ASCIIEncoding ^
textConverter =
gcnew ASCIIEncoding();
RijndaelManaged ^
myRijndael =
gcnew RijndaelManaged();
myRijndael->
Key =
Key;
myRijndael->
IV =
IV;
ICryptoTransform ^
encryptor =
myRijndael->
CreateEncryptor(Key, IV);
MemoryStream ^
msEncrypt =
gcnew MemoryStream();
CryptoStream ^
csEncrypt =
gcnew CryptoStream(msEncrypt, encryptor, CryptoStreamMode::
Write);
csEncrypt->
Write(encrypted, 0
, encrypted->
Length);
csEncrypt->
FlushFinalBlock();
encrypted =
msEncrypt->
ToArray();
return
encrypted;
}
array<
unsigned
char
>
^
Decrypter(array<
unsigned
char
>
^
encrypted, String ^
keyPhrase)
{
array<
unsigned
char
>
^
Key =
gcnew array<
unsigned
char
>
(24
);
array<
unsigned
char
>
^
IV =
gcnew array<
unsigned
char
>
(16
);
GenerateKey(keyPhrase, Key, IV);
array<
unsigned
char
>
^
fromEncrypt;
RijndaelManaged ^
myRijndael =
gcnew RijndaelManaged();
ASCIIEncoding ^
textConverter =
gcnew ASCIIEncoding();
myRijndael->
Key =
Key;
myRijndael->
IV =
IV;
ICryptoTransform ^
decryptor =
myRijndael->
CreateDecryptor(Key, IV);
MemoryStream ^
msDecrypt =
gcnew MemoryStream(encrypted);
CryptoStream ^
csDecrypt =
gcnew CryptoStream(msDecrypt, decryptor, CryptoStreamMode::
Read);
fromEncrypt =
gcnew array<
unsigned
char
>
(encrypted->
Length);
csDecrypt->
Read(fromEncrypt, 0
, fromEncrypt->
Length);
return
fromEncrypt;
}
String ^
Crypter(String ^
original, String ^
keyPhrase)
{
array<
unsigned
char
>
^
Key =
gcnew array<
unsigned
char
>
(24
);
array<
unsigned
char
>
^
IV =
gcnew array<
unsigned
char
>
(16
);
GenerateKey(keyPhrase, Key, IV);
ASCIIEncoding ^
textConverter =
gcnew ASCIIEncoding();
RijndaelManaged ^
myRijndael =
gcnew RijndaelManaged();
array<
unsigned
char
>
^
encrypted;
array<
unsigned
char
>
^
toEncrypt;
myRijndael->
Key =
Key;
myRijndael->
IV =
IV;
ICryptoTransform ^
encryptor =
myRijndael->
CreateEncryptor(Key, IV);
MemoryStream ^
msEncrypt =
gcnew MemoryStream();
CryptoStream ^
csEncrypt =
gcnew CryptoStream(msEncrypt, encryptor, CryptoStreamMode::
Write);
toEncrypt =
textConverter->
GetBytes(original);
csEncrypt->
Write(toEncrypt, 0
, toEncrypt->
Length);
csEncrypt->
FlushFinalBlock();
encrypted =
msEncrypt->
ToArray();
return
Convert::
ToBase64String(encrypted);
}
String ^
Decrypter(String ^
encryptedString, String ^
keyPhrase)
{
array<
unsigned
char
>
^
Key =
gcnew array<
unsigned
char
>
(24
);
array<
unsigned
char
>
^
IV =
gcnew array<
unsigned
char
>
(16
);
GenerateKey(keyPhrase, Key, IV);
array<
unsigned
char
>
^
encrypted =
Convert::
FromBase64String(encryptedString);
array<
unsigned
char
>
^
fromEncrypt;
RijndaelManaged ^
myRijndael =
gcnew RijndaelManaged();
ASCIIEncoding ^
textConverter =
gcnew ASCIIEncoding();
myRijndael->
Key =
Key;
myRijndael->
IV =
IV;
ICryptoTransform ^
decryptor =
myRijndael->
CreateDecryptor(Key, IV);
MemoryStream ^
msDecrypt =
gcnew MemoryStream(encrypted);
CryptoStream ^
csDecrypt =
gcnew CryptoStream(msDecrypt, decryptor, CryptoStreamMode::
Read);
fromEncrypt =
gcnew array<
unsigned
char
>
(encrypted->
Length);
csDecrypt->
Read(fromEncrypt, 0
, fromEncrypt->
Length);
return
textConverter->
GetString(fromEncrypt);
}
int
main(array<
System::
String ^>
^
args)
{
FileStream ^
fs =
gcnew FileStream("c:
\\
test.zip"
, FileMode::
Open);
BinaryReader ^
br =
gcnew BinaryReader(fs);
FileStream ^
fsw =
gcnew FileStream("c:
\\
test.cry"
, FileMode::
CreateNew);
BinaryWriter ^
bw =
gcnew BinaryWriter(fsw);
try
{
bw->
Write(Crypter(br->
ReadBytes((int
)fs->
Length), "code secret"
));
}
catch
(Exception^
)
{
}
finally
{
br->
Close();
fs->
Close();
bw->
Close();
fsw->
Close();
}
fs =
gcnew FileStream("c:
\\
test.cry"
, FileMode::
Open);
br =
gcnew BinaryReader(fs);
fsw =
gcnew FileStream("c:
\\
test2.zip"
, FileMode::
CreateNew);
bw =
gcnew BinaryWriter(fsw);
try
{
bw->
Write(Decrypter(br->
ReadBytes((int
)fs->
Length), "code secret"
));
}
catch
(Exception^
)
{
}
finally
{
br->
Close();
fs->
Close();
bw->
Close();
fsw->
Close();
}
return
0
;
}
Les fichiers de configuration sont des fichiers XML qui contiennent la configuration de notre exécutable. Ils doivent se situer dans le même répertoire que l'exécutable.
Visual C++ ne gère pas automatiquement les fichiers de configuration comme app.config. Tant est si bien que quand on essaie de les utiliser, à chaque chargement de valeur, on obtient une chaine vide.
Ceci est expliqué par le fait que Visual C++ ne copie pas automatiquement le fichier app.config dans le répertoire de l'exécutable (debug par exemple).
Il faut donc le faire manuellement ou bien se servir des événements après génération.
Aller dans les propriétés du projet -> événement de génération -> événement après génération. Et modifier la ligne de commande par :
copy app.config "$(TargetPath).config"
Tout d'abord, il faut créer le fichier de configuration app.config. Le plus simple ensuite est de travailler avec la section appSettings qui est gérée par le ConfigurationManager.
Dans le fichier, ajouter des clés et des valeurs comme ceci :
<configuration>
<appSettings>
<add key="nom" value="pyright"/>
<add key="prenom" value="nico"/>
</appSettings>
</configuration>
Ensuite, on peut y accéder ainsi :
String ^
nom =
Configuration::ConfigurationManager::
AppSettings["nom"
];
String ^
prenom =
Configuration::ConfigurationManager::
AppSettings["prenom"
];
Console::
WriteLine("Je m'appelle {0} {1}"
, prenom, nom);
N'oubliez pas d'ajouter la référence à System.Configuration.
Il peut être utile de pouvoir modifier le fichier de configuration depuis son application. Voici comment faire :
String ^
prenom =
Configuration::ConfigurationSettings::
AppSettings["prenom"
];
Console::
WriteLine(prenom);
Configuration::
Configuration ^
config =
Configuration::ConfigurationManager::
OpenExeConfiguration(Configuration::ConfigurationUserLevel::
None);
config->
AppSettings->
Settings->
Remove("prenom"
);
config->
AppSettings->
Settings->
Add("prenom"
, "Nouveau prenom"
);
config->
Save(Configuration::ConfigurationSaveMode::
Modified);
Configuration::ConfigurationManager::
RefreshSection("appSettings"
);
prenom =
Configuration::ConfigurationSettings::
AppSettings["prenom"
];
Console::
WriteLine(prenom);
On connait les fichiers de configuration d'application (app.config) (voir Comment lire une valeur dans un fichier de configuration).
Il est très pratique d'y stocker les informations de configuration de l'application. Il peut être tentant d'y stocker par exemple des informations sensibles (un login/mot de passe, ou une chaine de connexion de base de données par exemple).
Cependant, ces informations doivent être distribuées avec l'exécutable et donc librement consultable sous format XML.
Comment faire pour utiliser ce principe de configuration tout en conservant une sécurité adéquate ?
Plusieurs solutions viennent à l'esprit, comme d'implémenter un système de cryptage, à l'aide d'une clé d'encryption, comme par exemple grâce à l'algo de rijndael. Ceci implique d'avoir une méthode qui encrypte et qui décrypte les informations.
On peut également utiliser un mécanisme prévu par le framework.net pour un système d'encryption automatique en RSA.
Pour ce faire, on va déplacer l'écriture de la configuration dans le fichier machine.config. En utilisant toujours appsettings, ouvrez le fichier machine.config et si la section n'existe pas, créez la ainsi :
<appSettings>
<add
key
=
"login"
value
=
"nico"
/>
<add
key
=
"pwd"
value
=
"abcd"
/>
</appSettings>
Pour lire ces informations, on peut utiliser ce bout de code :
AppSettingsSection ^
section =
ConfigurationManager::
OpenMachineConfiguration()->
AppSettings;
String ^
login =
""
;
String ^
pwd =
""
;
if
(section->
Settings["login"
] !=
nullptr
)
login =
section->
Settings["login"
]->
Value;
if
(section->
Settings["pwd"
] !=
nullptr
)
pwd =
section->
Settings["pwd"
]->
Value;
Console::
WriteLine("Login : {0} - Mot de passe : {1}"
, login, pwd);
La méthode statique OpenMachineConfiguration nous permet de lire dans le machine.config.
Lancez ensuite la commande :
aspnet_regiis.exe -pe "appSettings" -pkm
On se retrouve avec un fichier machine.config, dont la section appSettings ressemble désormais à ça :
<appSettings
configProtectionProvider
=
"RsaProtectedConfigurationProvider"
>
<EncryptedData
Type
=
"http://www.w3.org/2001/04/xmlenc#Element"
xmlns
=
"http://www.w3.org/2001/04/xmlenc#"
>
<EncryptionMethod
Algorithm
=
"http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
/>
<KeyInfo
xmlns
=
"http://www.w3.org/2000/09/xmldsig#"
>
<EncryptedKey
xmlns
=
"http://www.w3.org/2001/04/xmlenc#"
>
<EncryptionMethod
Algorithm
=
"http://www.w3.org/2001/04/xmlenc#rsa-1_5"
/>
<KeyInfo
xmlns
=
"http://www.w3.org/2000/09/xmldsig#"
>
<KeyName>
Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>
mCMSEOwI/sofISqaMxpaRM53IsBizjlBfsSzDAkzrGihm41P6R072
FdkTkurkKBmyMjsXAI5oXlwYno7Ob8jws5i75yOjYWcy6y+uqEDTwfgIFt4kguVpEPbMlo6iR3dC0ipwmk0Eoz/TenDnSCt/e+cmO2FEQXfqYulNRq9bDA=</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>
TE6AddoMEjIwcAPi/mF0BBZHXfN2DZbaXzqK3SajSlv++HvGGdB
ohgRPCboO69d5PLxPo3NjgMB5rEzqiGL49CTeapUtfNAzffa1owXzAjzQJnH/2WP3nrRci8qLhzOUYU3urGokAbN1zwiGY2adkA==</CipherValue>
</CipherData>
</EncryptedData>
</appSettings>
qui est tout de suite moins compréhensible.
Et ce qui est très pratique, c'est que sans aucune modification de code, mon application va être capable de déchiffrer cette section, et retrouver mon mot de passe par exemple.
Relancez l'application, et le tour est joué.
static
String ^
RemplaceCaracteresAccentues(String ^
input)
{
array<
unsigned
char
>^
objBytes =
System::Text::Encoding::
GetEncoding(1251
)->
GetBytes(input);
return
System::Text::Encoding::
ASCII->
GetString(objBytes);
}
int
main()
{
Console::
Write("Entrez votre chaîne de carcatères : "
);
String ^
strResultat =
RemplaceCaracteresAccentues(Console::
ReadLine());
Console::
WriteLine("Résultat : "
+
strResultat);
return
0
;
}