2009-05-10 5 views
3

Je dois utiliser FormatMessage() pour un projet, mais je n'aime pas son interface effrayante. Est-ce que quelqu'un sait d'une façade qui range tout en tenant compte des paramètres de remplacement?Façade sûre/flexible pour Windows FormatMessage

Je viens de lire la second part of the FastFormat introduction, et je envisage d'écrire une extension pour FormatMessage() (ou demander à l'équipe du projet FastFormat s'ils ont un dans les travaux), mais je suis désireux d'obtenir quelque chose dès que possible, donc s'il y a quelque chose d'autre décent là-bas je serais probablement attraper à la place.

Ce que je veux est d'être capable d'écrire un code tel que:

HINSTANCE netevent = ::LoadLibrary("netevent.dll"); 
std::string msg = LookupError(netevent, EVENT_SERVICE_START_FAILED_II, 
    "child-svr", "parent-svr", "ship happens"); 
::puts(msg.c_str()); 

Ce qui donnerait le résultat:

The child-svr service depends on the parent-svr service which failed to start be cause of the following error: 
ship happens 

L'enveloppe actuelle, je l'ai construit a l'interface:

Il y a deux problèmes avec ceci:

  • Il est de type-safe, car il est facile de passer tout type - int, std::string, void* - ce n'est pas const char*
  • Il est facile de désadaptation le nombre d'arguments avec le nombre requis par la chaîne de format représentant l'erreur

Compte tenu des capacités de FastFormat en termes de type sécurité, je veux savoir s'il y a un moyen de suivre ses mécanismes pour faire face à FormatMessage().

+3

Vous voulez être capable d'écrire quelque chose comme ... quelle? –

+0

Doh! Pardon. Va résoudre ce problème momentanément ... – dcw

Répondre

1

Comme le nombre de paramètres à insérer dans la chaîne de format ne peut pas être vérifié par le compilateur, il est impossible de le rendre vraiment sûr au moment de la compilation.

Vous pouvez obtenir la plupart du chemin en ayant juste quelques surcharges pour différents nombres de paramètres insérés, puis en spécifiant les valeurs insérées avec quelque chose de flexible comme boost::any. Ainsi, la surcharge pour les deux paramètres serait:

std::string FormatMessage(HINSTANCE hinst, DWORD id, const boost::any &arg1, const boost::any &arg2); 

Lorsque vous récupérez la valeur de arg1, coup de pouce jetteront si vous essayez d'obtenir le mauvais type, vous avez juste besoin d'examiner la chaîne de format et essayer d'obtenir le type requis de chaque argument.

Vous pouvez également utiliser des modèles et std :: ostringstream (ou boost :: lexical_cast) pour une version très flexible; encore une fois il y aurait une surcharge pour permettre le nombre d'arguments pour faire varier, voici donc la version unique argument:

template <class TArg1> 
std::string FormatMessage(HINSTANCE hinst, DWORD id, const TArg1 &arg1) 
{ 
    std::ostringstream arg1Stream; 
    arg1Stream << arg1; 
    std::string arg1String = arg1Stream.str(); 

    DWORD_PTR argArray = reinterpret_cast<DWORD_PTR>(arg1String.c_str()); 

    // ... etc 
} 

De cette façon, vous pouvez obtenir une chaîne de chaque argument tant que le type passé peut être diffusé en continu, et rien d'autre ne devrait être requis tant que les chaînes de format n'attendent que des chaînes à insérer.

+1

Idée intéressante. FormatMessage() nécessite soit une va_list d'arguments, soit un tableau d'arguments de 32 bits. Donc, dans le premier cas, nous aurions besoin d'une fonction variadique, bien qu'elle puisse être invoquée dans une fonction comme celle que vous décrivez, et que boost :: any gère la sécurité de type. Alternativement, nous pourrions construire le tableau d'arguments directement. Mais l'une des raisons pour lesquelles je m'intéressais à FastFormat est sa capacité à travailler avec des arguments de type arbitraire, ce qui signifierait que n'importe quel type pourrait être passé, puis converti en char * avant d'appeler FormatMessage(). Je ne pense pas boost :: tout le permettrait – dcw

+0

Voir mise à jour, quelque chose de plus flexible. –

0

The C++ Format library permet de formater les messages d'erreur Windows natifs correspondant aux codes d'erreur renvoyés par GetLastError() et les messages d'erreur POSIX correspondant aux erreurs données par errno.Par exemple:

// This throws a WindowsError with the description 
// cannot open file 'madeup': The system cannot find the file specified. 
// or similar (system message may vary). 
const char *filename = "madeup"; 
LPOFSTRUCT of = LPOFSTRUCT(); 
HFILE file = OpenFile(filename, &of, OF_READ); 
if (file == HFILE_ERROR) 
    throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename); 

de Windows message d'erreur est obtenue en utilisant la fonction API FormatMessage. Vous pouvez également formater un message d'erreur avec fmt::format_windows_error qui ne génère pas d'exception. Voir System Errors pour plus de détails. Je suis l'auteur du format C++