Comment utiliser VS C++ GetEnvironmentVariable aussi proprement que possible?
(Ce n'est pas tellement un problème pour un exercice de pédantisme, donc voilà.)
J'ai fait un petit programme qui est originaire de mon système d'exploitation linux, mais je pense que c'est utile seulement exister sur ma machine Windows. Donc, j'aimerais accéder à Windows' variables d'environnement, et MSDN cite un exemple comme ceci:
const DWORD buff_size = 50;
LPTSTR buff = new TCHAR[buff_size];
const DWORD var_size = GetEnvironmentVariable("HOME",buff,buff_size);
if (var_size==0) { /* fine, some failure or no HOME */ }
else if (var_size>buff_size) {
//OK, so 50 isn't big enough.
if (buff) delete [] buff;
buff = new TCHAR[var_size];
const DWORD new_size = GetEnvironmentVariable("HOME",buff,var_size);
if (new_size==0 || new_size>var_size) { /* *Sigh* */ }
else { /* great, we're done */ }
}
else { /* in one go! */ }
Ce n'est pas aussi belle (pour moi) à l'aide de la fonction getenv et vérifier simplement pour un pointeur null. Je voudrais aussi préfèrent ne pas allouer dynamiquement de la mémoire depuis que je suis juste essayer de faire fonctionner le programme sur Windows ainsi que sur mon système d'exploitation linux, ce qui signifie que ce MME code a jouer gentiment avec nix code. Plus précisément:
template <class T> //let the compiler sort out between char* and TCHAR*
inline bool get_home(T& val) { //return true if OK, false otherwise
#if defined (__linux) || (__unix)
val = getenv("HOME");
if (val) return true;
else return false;
#elif defined (WINDOWS) || defined (_WIN32) || defined (WIN32)
//something like the MS Code above
#else
//probably I'll just return false here.
#endif
}
Donc, j'avais à allouer sur le tas universellement ou faire un #ifdef
dans les appels de fonctions pour libérer de la mémoire. Pas très jolie.
Bien sûr, j'aurais pu le allouées 'chamois' sur la pile en premier lieu, mais alors que j'aurais du créer un nouveau TCHAR[]
si 'buff_size" n'était pas assez grand pour mon premier appel à GetEnvironmentVariable. Mieux, mais si j'étais un pédant et ne voulait pas aller autour de la création de superflu tableaux? Toutes les idées sur quelque chose de plus esthétique?
Je ne suis pas bien informé, donc quelqu'un pourrait-il me réjouis délibérément forcer GetEnvironmentVariable à l'échec afin d'obtenir une chaîne de caractères de taille? Est-ce quelqu'un voit un problème avec:
const DWORD buff_size = GetEnvironmentVariable("HOME",0,0);
TCHAR buff[buff_size];
const DWORD ret = GetEnvironmentVariable("HOME",buff,buff_size);
//...
Autres idées ou des suggestions? (Ou des corrections d'erreurs flagrantes?)
Mise à JOUR:
Beaucoup d'informations utiles ci-dessous. Je pense que le meilleur pari pour ce que je suis en train de faire est d'utiliser un static char[]
comme:
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
}
C'est peut-être pas la façon la plus élégante de le faire, mais c'est probablement la façon la plus simple de synchroniser jusqu'à ce que je veux faire entre les *nix et Windows. (Je vais aussi vous soucier de la prise en charge Unicode plus tard.)
Merci pour l'aide les gars.
std::basic_string<TCHAR>
. Qui résout le problème de gestion de mémoire. Cependant, veuillez noter que %HOME%
n'est pas définie par défaut. Vous pourriez être à la recherche pour %USERPROFILE%
, ` %APPDATA% " ou %LOCALAPPDATA%
.Je ne suis pas familier avec les variables d'environnement Windows, j'ai donc manqué à ce que je savais! Ouais,
%USERPROFILE%
est ce que j'ai le plus besoin. Retour de la chaîne de valeur sonne comme une bonne idée! Je peux juste renvoyer une chaîne vide comme un échec de l'indicateur. Je vais lui donner un aller lorsque j'ai peut-être pris la peine de descendre de mon ordinateur portable...
OriginalL'auteur Zorawar | 2010-11-09
Vous devez vous connecter pour publier un commentaire.
Bien sûr, si vous voulez ASCII, remplacer
wstring
avecstring
etGetEnvironmentVariableW
avecGetEnvironmentVariableA
.EDIT: Vous pouvez également créer des
getenv
vous-même. Cela fonctionne parce queBien sûr, il serait probablement une bonne idée d'utiliser le caractère large versions de tout cela si vous voulez maintenir le support de l'unicode.
Dans la plupart des TSL implémentations, si vous
resize
à une valeur inférieure de la mémoire n'est pas réaffecté. Par conséquent, il n'y a qu'une seule affectation ici.Je suppose que vous pouvez le faire avec une pile tampon alloué mais 131,070 octets est d'une très grande valeur à mettre sur la pile.
Ouais, c'est pourquoi je ne veux pas de deuxième deviner la variable d'environnement taille et l'utilisation de cette mémoire pour aucune raison! Pas de réponses à propos de mon idée de forcer
GetEnvironmentVariable
à l'échec délibérément afin que je puisse obtenir une taille variable. Je suppose que c'est pas une bonne idée, ou redondantes...Le délibéré de l'échec est, je crois, le "normal", C-comme moyen d'accéder à cette API. Cela est vraiment nécessaire de trouver un compromis. Vous passez un peu plus de mémoire, mais vous vous enregistrez l'appel de la fonction à deux reprises.
GetEnvironmentVariable
a un montant raisonnable des frais généraux de tous sur son propre. Vous pourriez probablement la conception d'une version plus rapide sur le dessus de GetEnvironmentStrings (Parce qu'il ne fait que retourner un pointeur vers l'environnement de processus de bloc), mais je pensais à quelque chose comme ce n'était pas critique pour les performances du code.OriginalL'auteur Billy ONeal
VC++ implémente la fonction getenv dans stdlib.h, voir, par exemple, ici.
Je ne sais pas. Nous utilisons la fonction getenv ici pour nos fenêtres s'appuie. Je ne pense pas que nous avons tout spécial drapeaux du compilateur ou de quoi que ce soit.
fait partie de l'API Win32, et peut être utilisé à partir d'autres langues que le C.
getenv
est une partie de C, et va bien évidemment faire appelGetEnvironmentVariable
. De même, l'API Win32 ne comprend pasoperator new
oumalloc
, mais ne contiennentGlobalAlloc
. Le "obsolète" message peut se produire lorsque vous appelezputenv
, qui est pas un standard C de la fonction.OK, ça a du sens. Pour référence, l'avertissement que je vous est
C4996: 'getenv': This function or variable may be unsafe. Consider using _dupenv_s instead...
Théoriquement, la fonction getenv est dangereux car il renvoie un pointeur vers une structure interne qui pourrait par la suite être modifié, par exemple, par le biais de putenv. Puisque vous travaillez sur un "gentil petit programme" pour une utilisation personnelle, j'imagine que vous êtes plus intéressés à faire bouger les choses rapidement plutôt que théorique des problèmes de sécurité, donc je pense que c'est OK pour utiliser la fonction getenv. Voir msdn.microsoft.com/en-us/library/8ef0s5kh%28v=VS.80%29.aspx pour plus d'info sur l'avertissement. C'est comme si vous #define _CRT_SECURE_NO_WARNINGS l'avertissement s'en aller.
OriginalL'auteur David Norman
Ce n'était pas la question d'origine, mais il pourrait la peine d'ajouter le MFC façon à ce fil de discussion pour référence:
OriginalL'auteur user1228651
N'est pas la peine.
%HOME%
est un chemin sur Windows, et doit être utilisable par tous les moyens raisonnables programmes. Par conséquent, il ira parfaitement dans unWCHAR[MAX_PATH]
. Vous n'avez pas besoin de traiter le cas limite où il est plus que cela - si c'est la plus longue, la plupart des fonctions de fichier de la rejeter toute façon, alors vous pourriez aussi bien échouer tôt.Cependant, ne pas suppose que vous pouvez utiliser un
TCHAR[MAX_PATH]
ou unchar[MAX_PATH]
. Vous n'avez pas de contrôle sur le contenu de%HOME%
; il contient le nom des utilisateurs. Si c'est le "André" (c'est à dire non ASCII), vous devez stocker%HOME%
dans unWCHAR[MAX_PATH]
.Oh, ne me dites pas "Ne pas déranger": je suis ce fermer pour ne pas s'embêter avec la WinAPI à tous! Je n'ai pas utilisé correctement VC++ dans de nombreuses années, mais deosn pas
TCHAR
résolu àchar
ouwchar_t
? (Il y a unWCHAR
trop!?)WCHAR n'est pas un C ou C++ type de données. C'est un win32 type de données. Win32 n'est pas une api en C, c'est une langue agnostique de l'API. Par conséquent, ils ne peuvent pas compter sur c de type char et wchar_t. Sur la plupart des plates-formes, CHAR étend à
char
et WCHAR étend àwchar_t
. Si vous compilez avec le support de l'Unicode TCHAR étendra à WCHAR, et si vous compilez sans support de l'Unicode TCHAR étendra à CHAR.Droite, je vois. Merci pour la clarification.
ONeal: 32 KO limite ne s'applique que pour les chemins avec un
\\?` prefix, which you're unlikely to see in a
%HOME% variable - il des pauses d'autres hypothèses communes.OriginalL'auteur MSalters
La suggestion que vous avez faite à la fin de votre post, c'est la bonne façon de faire appel une fois pour obtenir nécessaire taille de la mémoire tampon, puis de nouveau à réellement obtenir les données. Beaucoup de l'Api Win32 travailler de cette façon, c'est déroutant au premier abord, mais commun.
Une chose que vous pourriez faire est de passer à un meilleur jugement de la mémoire tampon et sa taille sur le premier appel, et appeler de nouveau, si cela échoue.
GetEnvironmentVariable
juste pour être un peu plus pointilleux sur l'allocation de mémoire? Quel est le livre de base (C++) manière de procéder?C'est Win32, qui est une API en C, pas du C++. Microsoft rend les règles ici, et c'est la "bonne" façon de le faire. Si vous voulez juste un appel, vous aurez à utiliser un tampon assez grand pour tout ce que vous souhaitez traiter. Vous avez encore besoin de vérification d'erreur à l'appel de l'API de toute façon. Le max est de 32767 y compris la valeur null, donc pas de point à l'aide d'un tampon plus grand que cela.
Gardez à l'esprit qu'il existe une condition de concurrence entre les deux appels. Dans une application simple, c'est un non-problème, mais strictement de la mise en œuvre correcte n'est pas spécifiquement deux appels, mais une boucle qui appelle jusqu'à ce qu'il rencontre le succès ou une erreur. Le devrait à l'exécution de cette boucle produit deux appels, mais si quelqu'un modifie la variable entre les deux appels, la boucle va faire un troisième appel à une conséquence-mise à jour de la mémoire tampon.
Le tampon et sa taille sont à la fois basée sur la pile. Seulement un très inhabituel application va vous permettre soit de la valeur à être modifié entre les deux appels, même si plusieurs threads existent.
OriginalL'auteur Steve Townsend