(Ce n'est pas tant de problème qu'un exercice de pédanterie, voilà donc.)
J'ai fait un joli petit programme qui est originaire de mon système d'exploitation Linux, mais je pense que c'est assez utile pour Il existe aussi sur ma machine Windows. Ainsi, j'aimerais accéder aux variables d'environnement de Windows et MSDN cite un exemple comme celui-ci: P>
inline const char* get_home(void) { // inline not required, but what the hell.
#if defined (__linux) || (__unix)
return getenv("HOME");
#elif defined (WINDOWS) || defined (WIN32) || defined (_WIN32)
static char buff[MAX_PATH];
const DWORD ret = GetEnvironmentVariableA("USERPROFILE",buff,MAX_PATH);
if (ret==0 || ret>MAX_PATH)
return 0;
else
return buff;
#else
return 0;
#endif
}
5 Réponses :
VC ++ implémente getenv dans stdlib.h, voir, par exemple, ici . p>
Je ne suis pas sur ma machine à Windows en ce moment, mais cela craignait quelque chose sur la dépréciation lorsque j'ai essayé de compiler avec Getenv (sauf si je n'étais en retard et mal interprété l'erreur). Je ne payais pas trop d'attention et je suis à moitié prévu que MS soit obtus à ce sujet, même si Getenv est dans la bibliothèque standard. Pourquoi MS a-t-elle une entrepriseVironmentVariable à la première place?
Je ne sais pas. Nous utilisons Getenv ici pour nos constructions Windows. Je ne pense pas que nous ayons des drapeaux spéciaux de compilateur ou quoi que ce soit.
@Zorawar: getenvironmentvariable code> fait partie de l'API Win32 et peut être utilisé à partir d'autres langues que c. getenv code> fait partie de C et va évidemment appeler getenvironmentvariable < / code>. De même, l'API Win32 n'inclut pas Opérateur Nouveau code> ou MALLOC code>, mais contient globalalloc code>. Le message "obsolète" peut arriver lorsque vous appelez putenv code>, qui est pas i> une fonction C standard C.
@Msalters: OK, ça a du sens. Pour référence, l'avertissement que je reçois est C4996: 'getenv': cette fonction ou une variable peut être dangereuse. Pensez à utiliser _DUPENV_S à la place ... code>
Théoriquement, Getenv est dangereux car il renvoie un pointeur sur une structure interne qui pourrait ultérieurement être modifiée, par exemple, par Putenv. Puisque vous travaillez sur un «joli petit programme» pour un usage personnel, j'imagine que vous êtes plus intéressé à obtenir des choses rapidement plutôt que des préoccupations de sécurité théoriques, alors je pense qu'il est correct d'utiliser Getenv. Voir msdn.microsoft.com/en-us /Library/8EF0SS5KH%28V=VS.80%29.aspx Pour plus d'informations sur l'avertissement. On dirait que si vous #define _crt_secure_no_warnings L'avertissement disparaîtra.
@David: Oh, je vois. Le compilateur VC ++ essaie simplement d'être trop utile plutôt que de simplement coller aux problèmes syntaxiques. Dans mon cas, je ne pense pas que ce soit une grande partie d'une question depuis la façon dont j'utilise la variable d'environnement ne provoquera aucune difficulté si quelque chose d'impair l'arrive entre la récupération et l'utiliser, donc je pourrais juste rester avec getenv code> dans la pratique.
const char * WinGetEnv(const char * name)
{
const DWORD buffSize = 65535;
static char buffer[buffSize];
if (GetEnvironmentVariableA(name, buffer, buffSize))
{
return buffer;
}
else
{
return 0;
}
}
redimensionner. Oui, ce serait plus facile ... (putain-le). Edit: Bien que, il gâche toujours avec une allocation de mémoire (la dignité sauvée légèrement?). Existe-t-il un moyen de le faire en allouant la mémoire une seule fois, ou est-ce une question stupide?
@Zorawar: Dans la plupart des implémentations STL, si vous redimensionner code> à une mémoire inférieure n'est pas réaffectée. Par conséquent, il n'y a qu'une seule allocation ici.
Je suppose que vous pourriez le faire avec un tampon attribué à la pile, mais 131 070 octets est une valeur terriblement grande pour mettre sur la pile.
Oui, c'est pourquoi je ne veux pas dilater la taille de la variable de l'environnement et utiliser toute cette mémoire sans raison! Pas de réponses sur mon idée de forçage getenvironmentvariable code> pour échouer délibérément afin que je puisse obtenir une taille de variable. Je suppose que ce n'est pas une bonne idée, ni de ...
@Zorawar: L'échec délibéré est, je crois, la manière "normale" de type C d'accès à cette API. Faire ceci est vraiment un compromis. Vous dépensez plus de mémoire mais vous économisez appel à deux reprises. getenvironmentvariable code> a une quantité raisonnable de frais généraux tout seul. Vous pouvez probablement concevoir une version plus rapide au-dessus de GetenviricielsStrings (car elle renvoie simplement un pointeur sur le bloc Environnement de processus), mais j'ai supposé que quelque chose comme celui-ci n'était pas un code critique de performance.
@Billy: appeler getenvironmentvariable code> est arrivé à deux fois pour moi et je suppose que ce serait probablement la pire option de termes pratiques. Mais le fait que getenvironmentvariable code> doit faire plus de travail que ce que getenv code> ferait est vraiment de conduire le point à la maison!
@Zorawar: C'est probablement pas i> faire plus de travail que getenv code>. Ce n'est pas parce qu'une API est plus simple que cela fait moins de travail. Bien que; Les variables d'environnement sont une plus grande affaire sur * Nix Machines, donc je ne serais donc pas surprise s'ils ont une manière LG (n) de le faire. OTOH, il est difficile de fournir un véritable
@Zorawar: Ce que je veux dire, c'est que vous pouvez utiliser un tampon alloué statiquement alloué et implémenter getenv code> vous-même, car les appels vers getenv code> détruisent les valeurs de retour des appels précédents vers getenv code>. Cependant, si vous avez mis en œuvre exactement i> getenv code> vous lanceriez une assistance Unicode sur la fenêtre ,.
@Billy: Un peu de messages avec le code m'a amené à la conclusion que cela fait quelque chose comme ceci est probablement le meilleur pari (je vais mettre à jour ma question lorsque je le fais et vérifie bien sûr). Tout recommandé DWORD code> Valeur de l'appel à getenvironmentVariable code>? Comme vous l'avez souligné dans le lien indiqué ci-dessus, je pourrais utiliser 32767 * Taille de (...) code>, mais existe-t-il une limite pratique inférieure à celle que je devrais probablement rester?
@Zorawar: Comme le souligne Msalters, vous attendez une voie ici. La plupart des API Windows supposent que vous utilisez des chemins Win32 plutôt que des chemins NT, ce qui limite la taille totale du chemin utilisable sur max_path code>, qui (je crois) est de 260 caractères.
Utilisation d'un statique code> ici rend l'application non pas en sécurité. Cette approche est de savoir pourquoi VC ++ amorcate de nombreuses fonctions CRT (voir aussi strtok code>). Je ne suivrais pas les recommandations ici personnellement, pas seulement pour cette raison - la grande utilisation du tas par défaut, et la nécessité de mettre en œuvre à nouveau pour UNICODE semble également moins que idéale.
@Steve: getenv code> s de l'interface n'est pas en sécurité. Si vous avez plusieurs threads, tous messages avec l'environnement, il y a probablement un problème là-bas de toute façon. Je suis d'accord C'est une API terriblement conçue, mais c'est vrai de nombreuses API conçues il y a presque 30 ans.
Je dirais que cela est encore plus raisonnable de ne pas respirer une nouvelle vie dans un modèle de conception d'API cassé (votre wingetenv code> code). Par conséquent, les solutions utilisant des données à base de piles sont préférables à moi. Je n'avais pas l'intention de dire "il suffit d'utiliser getenv code> '.
Que se passe-t-il lorsque la valeur d'une variable d'environnement est une chaîne de longueur zéro? Comment distinguer cela d'une variable d'environnement qui n'est pas définie du tout et d'une condition d'erreur?
Ne vous inquiétez pas. Cependant, do % Home% Code> est un chemin sous Windows et doit être utilisable par tous les programmes raisonnables. Par conséquent, il ira dans un wchar [max_path] code>. Vous n'avez pas besoin de faire face à l'affaire Edge où elle est plus longue que celle-là - si elle est plus longue, la plupart des fonctions de fichiers le rejeteront de toute façon afin que vous puissiez aussi bien échouer tôt. P>
TCHAR [max_path] code> ou un char [max_path] code>. Vous n'avez pas de contrôle sur le contenu de % home% code>; Il contiendra le nom des utilisateurs. Si c'est "André" (c'est-à-dire non ASCII), vous devez stocker % home% code> dans un wchar [max_path] code>. P>.
En fait, sur NT, il n'est pas nécessaire de ne pas correspondre à max_path, bien que de nombreuses applications vont probablement casser si ce n'est pas le cas. ;) (La limite de NT est de 32k caractères) et bien sûr, une personne peut remplacer la valeur avec tout ce qu'ils veulent, même si le remplacement n'est pas un chemin valide.
Ooh, ne me dis pas "Ne te dérange pas": je suis Ceci i> près de ne pas déranger avec le Winapi du tout! Je n'ai pas correctement utilisé VC ++ depuis de nombreuses années, mais des "Code> TCHAR CODE> résolus à Char code> ou wchar_t code>? (Il y a un wcharner code> aussi !?)
@Zorawar: WCHAR n'est pas un type de données C ou C ++. C'est un type de données Win32. Win32 n'est pas une API C, il s'agit d'une API agnostique linguistique. Par conséquent, ils ne peuvent pas compter sur certains types C tels que CHAR et WCHAR_T. Sur la plupart des plates-formes, Char expandes à Char CODE> et WCHAR se développe à wchar_t code>. Si vous compilez avec la suppression d'unicode, TCHAR se développera à WCHAR, et si vous compilez sans support unicode, TCHAR se développera au char.
@Billy: Droite, je vois. Merci pour la clarification.
@Billy Oneal: la limite de 32kb ne s'applique que sur des chemins avec un préfixe \\? `` `` Préfixe, que vous êtes peu susceptible de voir dans un code>% à la maison% `variable - il casse d'autres hypothèses communes.
Juste pour être clair, la majeure partie de l'API sous-jacente n'a aucun problème avec les chemins plus longs que max_path code>. max_path code> est la limite supérieure du nom d'un chemin composant i>, mais le chemin peut combiner n'importe quel nombre de composants jusqu'à la taille d'un Unicode_string code> . Cependant, il y a beaucoup de confusion sur la question, car même dans Microsoft, les développeurs ne comprennent pas toujours cela, et il y a des parties de l'API système qui ne peuvent pas gérer les chemins plus longtemps que max_path code>. Ils sont définitivement possibles, cependant, et les applications correctement écrites peuvent et les utiliser.
Il convient également d'être considéré comme étant donné que%% à la maison pourrait être plus long que 255 caractères, même si le répertoire de base n'est pas autorisé par certains des composants qui le gèrent. % Home% n'est pas strictement requis par le système comme un nom de répertoire. C'est juste un nom de variable d'environnement, qui a également une limite supérieure de 64 Ko, si je comprends bien. Si votre code suppose qu'il s'intégrera à max_path, vous pouvez rencontrer des cas d'angle ou un comportement indéfini si un attaquant le rend intentionnellement plus long.
@Jonathangilbert: C'est un environnement malveillant et vous pouvez simplement échouer en toute sécurité là-bas. Ne faites pas le problème de sécurité pire, mais TerminerProcess code> est probablement acceptable.
@Msalters, d'accord, mais selon mon commentaire précédent, il n'est toujours pas "correct" pour assumer % home% code> ira dans wchar [max_path] code>. La meilleure expérience utilisateur, à mon avis, proviendrait du code qui attribue de manière dynamique des tampons pour % de path% code> et de tous les chemins dérivés, et rapporte avec précision les erreurs résultant des API en utilisant ces chemins. Les tampons de taille fixe constituent une pratique de programmation médiocre pour un certain nombre de raisons, y compris une utilisation de l'espace de sécurité et de pile d'exécution. De plus, toute limite de la longueur d'un chemin est «détenue» par le système et doit donc être appliquée par le système et non votre propre application.
La suggestion que vous avez faite à la fin de votre message est la bonne façon de le faire - appelez une fois pour obtenir la taille de la mémoire tampon requise, puis à nouveau pour obtenir les données. Beaucoup des API Win32 fonctionnent de cette façon, cela est confus au début mais commun. p>
Une chose que vous pourriez faire est de passer dans un tampon de bonne hypothèse et de sa taille sur le premier appel, puis appelez à nouveau si cela échoue. P>
Pourrais-je vraiment justifier deux appels vers GetenvironmentVariable Code> Juste pour être un peu plus pédant à propos de l'allocation de mémoire? Quel est le mode de manuel (C ++) de faire cela?
C'est Win32, qui est une API C, pas C ++. Microsoft rend les règles ici, et c'est le moyen «droit» de le faire. Si vous voulez juste un appel, vous devrez utiliser un tampon assez grand pour tout ce que vous souhaitez gérer. Vous avez toujours besoin d'erreur vérifier l'appel de l'API de toute façon. Le MAX est 32767 comprenant la null de terminaison, donc aucun point d'utilisation d'un tampon plus gros que cela.
Gardez à l'esprit qu'il existe une condition de course entre les deux appels. Dans une application simple, c'est un non-problème, mais une mise en œuvre strictement correcte n'est pas spécifiquement deux appels, mais une boucle qui l'appelle jusqu'à ce qu'elle rencontre du succès ou une erreur. L'exécution attendue i> de cette boucle produit deux appels, mais si une personne modifie la variable entre les deux appels, la boucle fera un troisième appel avec un tampon à jour correspondant.
Le tampon et sa taille sont à la fois basés sur la pile. Seule une application très inhabituelle permettra d'être modifiée soit entre les deux appels, même si plusieurs threads existent.
Pensez-vous que c'est exprès qu'elles écrivent dans la documentation que la contenue de tampon est indéfinie i> b> au lieu du contenu est pas touché i>? Citation: Si LPBuffer n'est pas assez grand pour tenir Les données, la valeur de retour correspond à la taille de la mémoire tampon, dans les caractères requises pour maintenir la chaîne et son caractère null de terminaison et le contenu de LPBuffer ne sont pas définis.
Ce n'était pas la question originale, mais cela pourrait valoir l'ajout de la voie MFC à ce fil pour référence:
CString strComSpec;
if (strComSpec.GetEnvironmentVariable(_T("COMSPEC")))
{
//Do your stuff here
}
En C ++, vous devez retourner la valeur sous forme de
std :: basic_string code>. Qui résout le problème de gestion de la mémoire. Cependant, veuillez noter que % home% code> n'est pas défini par défaut. Vous recherchez peut-être% userprofile% code>, `% appdata%` ou% localAppData% code>.Je ne connais pas les variables d'environnement Windows, alors j'ai fait défaut à ce que je savais! Ouais,
% userprofile% code> est ce dont j'ai besoin, cependant. Le retour de la valeur de chaîne ressemble à une bonne idée! Je peux simplement retourner une chaîne vide en tant qu'indicateur d'échec. Je vais lui donner un coup quand je peux être dérangé pour sortir de mon ordinateur portable ...