WinApi - GetLastError contre le Maréchal.GetLastWin32Error
J'ai testé beaucoup de choses. Mais je n'ai pas trouvé les inconvénients de ces 2!
Mais voir la accepté de répondre.
J'ai lu ici que l'appel GetLastError
en code managé est dangereux, parce que le Cadre pourrait interne "écraser" la dernière erreur. Je n'ai jamais eu de problèmes notables avec GetLastError
et il me semble que le .NET Framework est assez intelligent pour ne pas l'écraser. Donc j'ai quelques questions sur ce sujet:
- dans
[DllImport("kernel32.dll", SetLastError = true)]
ne leSetLastError
attribut de rendre le Cadre de stocker le code d'erreur pour l'utilisation deMarshal.GetLastWin32Error()
? - est là un exemple où la plaine
GetLastError
ne donne pas le résultat correct ? - dois-je vraiment à utiliser
Marshal.GetLastWin32Error()
? - est ce "problème" Framework version liés ?
public class ForceFailure
{
[DllImport("kernel32.dll")]
static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);
public static void Main()
{
if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
System.Console.WriteLine("It worked???");
else
{
//the first last error check is fine here:
System.Console.WriteLine(GetLastError());
System.Console.WriteLine(Marshal.GetLastWin32Error());
}
}
}
Produire des erreurs:
if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
Console.WriteLine("It worked???");
else
{
//bad programming but ok GetlLastError is overwritten:
Console.WriteLine(Marshal.GetLastWin32Error());
try
{
using (new FileStream("sdsdafsdfsdfs sdsd ", FileMode.Open)) { }
}
catch { }
Console.WriteLine(GetLastError());
}
if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
Console.WriteLine("It worked???");
else
{
//bad programming and Marshal.GetLastWin32Error() is overwritten as well:
Console.WriteLine(GetLastError());
try
{
using (new FileStream("sdsdafsdfsdfs sdsd ", FileMode.Open)) { }
}
catch { }
Console.WriteLine(Marshal.GetLastWin32Error());
}
//turn off concurrent GC
GC.Collect(); //doesn't effect any of the candidates
Console.WriteLine(" -> " + GetLastError());
Console.WriteLine(" -> " + GetLastError());
Console.WriteLine(Marshal.GetLastWin32Error());
Console.WriteLine(Marshal.GetLastWin32Error());
//when you exchange them -> same behaviour just turned around
Je ne vois pas la différence! Les deux se comportent de la même sauf Marshal.GetLastWin32Error
stocke les résultats à partir de l'App->CLR->WinApi appelle ainsi et GetLastError
stocke uniquement les résultats à partir de l'App->WinApi appels.
La Collecte des ordures ne semble pas appeler n'importe quel WinApi fonctions d'écraser le dernier code d'erreur
- GetLastError est thread-safe. SetLastError stocke un code d'erreur pour chaque thread appelant.
- depuis quand serait-GC exécuter dans mes filets ?
GetLastError
œuvres, il est possible que cela fonctionne dans tous les existants .NET Framework versions et des mises en œuvre. Ainsi, votre code fonctionne, mais ce n'est pas de prouver quoi que ce soit. .NET Framework, les développeurs sont libres de changer .NET mise en œuvre par la voie, queGetLastError
va s'arrêter une journée de travail.- .NET développeurs seule garantie, que
GetLastWin32Error
fonctionne correctement. Vous souhaitez utiliserGetlastError
hack, peut-être qu'il le sera toujours, mais c'est toujours un hack. Donc, la question est un peu philosophique: peut-on utiliser des hacks, si il n'est pas prouvé, qu'il est incorrect. - Ce serait un mauvais .NET Framework version. J'imagine que beaucoup de logiciel est en cours d'exécution avec la simple GetLastError parce que le programmeur n'ai pas entendu parler de Maréchal.GetLastWin32Error ou quoi que ce soit. Faire la mise à jour que vous avez mentionné serait briser tous ces logiciels.
- Peut-être que ce n'est pas convaincante exemple... de toute façon. Appel de l'API Win32 avec SetLastError=true. Les appels de l'API avec SetLastError=false. Appel GetLastWin32Error - il garde la dernière erreur de la part du premier appel d'API, par définition.
- Sur la bonne ou mauvaise .NET Framework version - encore une fois, c'est la philosophie. Vous n'avez pas à me convaincre (et personne d'autre, je l'espère) à utiliser GetLastError. Mais tout le monde n'a pas à vous convaincre d'utiliser GetLastWin32Error.
- Voir this. Je voulais savoir exactement si je dois ou ne pas avoir à les jeter tous mes utilisations de GetLastError dans le code existant. Actuellement, il n'est tout simplement pas de prouver qu'il est dangereux.
Vous devez vous connecter pour publier un commentaire.
Vous devez toujours utiliser la
Maréchal.GetLastWin32Error
. Le principal problème est le garbage collector. Si elle s'exécute entre l'appel deSetVolumeLabel
et l'appel deGetLastError
ensuite, vous recevrez la valeur faux, parce que le GC a sûrement écrasé le dernier résultat.Par conséquent, vous devez toujours spécifier le
SetLastError=true
dans le DllImport-Attribut:Cela garantit que le marhsallling stub appelle immédiatement après la fonction native de la "GetLastError" et le stocke dans le fil local.
Et si vous avez spécifié cet attribut ensuite, l'appel à
Maréchal.GetLastWin32Error
aura toujours de la valeur correcte.Pour plus d'infos voir aussi GetLastError et du code managé
Également une autre fonction de .NET peut changer les fenêtres "GetLastError". Voici un exemple qui produit des résultats différents:
Aussi, il semble que cela dépend de la CLR qui vous aide! Si vous compilez avec .NET2, il va produire "2 /0"; si vous passez .NET 4, il sera sortie "2 /2"...
De sorte qu'il est dépendait de la version CLR, mais vous ne devriez pas faire confiance à la maternelle
GetLastError
fonction; toujours utiliser leMarshal.GetLastWin32Error
.GetLastError
. Voir mon edit et un exemple de code.Marshal.GetLastWin32Error
. Mais c'est en fait la mauvaise programmation. Comme lorsque vous écrasez une variable et s'attendre à avoir de la valeur ancienne encore. Ce que je comprends de la dllimport chose est bien conçu dans .NET et l'utilisation deGetLastError
est de sauvegarder bien que les gens essaient de te faire peur de l'utiliser.Marshal.GetLastWin32Error()
etGetLastError()
sans autres appels entre les deux. Maintenant, si les deux appels de retour à un résultat différent, vous auriez prouvé qu'il y a une différence. Avec votre exemple, la différence est peut-être quenew FileStream(...)
est d'effectuer un Win32 appel. Cela pourrait même être unDllImport
avecSetLastError = true
!TL;DR
[DllImport(SetLastError = true)]
etMarshal.GetLastWin32Error()
Marshal.GetLastWin32Error()
immédiatement après l'échec de laWin32
appel et sur le même thread.Argumentation
Comme je l'ai lu, l'explication officielle pourquoi vous avez besoin de
Marshal.GetLastWin32Error
peut être trouvé ici:Pour le dire en d'autres mots:
Entre votre Win32 appel qui définit l'erreur, le CLR peut "insérer" autres appels Win32 qui pourrait remplacer l'erreur.
La spécification de
[DllImport(SetLastError = true)]
permet de s'assurer que le CLR récupère le code d'erreur avant que le CLR exécute tous les imprévus appels Win32.Pour accéder à cette variable, nous avons besoin d'utiliser
Marshal.GetLastWin32Error
.Maintenant ce que @Bitterblue trouvé, c'est que ces "insérée appels" ne se produisent pas souvent et qu'il ne pouvait pas en trouver. Mais ce n'est pas vraiment surpising. Pourquoi? Parce qu'il est extrêmement difficile de "boîte noire test" si
GetLastError
fonctionne de manière fiable:Il est un composant spécifique - le Garbage collector (GC) - qui est connu pour interrompre une .net thread si il y a la pression de la mémoire et de faire un peu de traitement sur ce thread (voir Ce qui se passe lors d'une collecte des ordures). Maintenant, si la GC ont été à l'exécution d'une faute Win32 appel, ce serait briser votre appel à
GetLastError
.Pour résumer, vous avez une multitude de facteurs inconnus qui peuvent influer sur la fiabilité de
GetLastError
. Vous aurez plus de chances de ne pas trouver un manque de sérieux problème lors du développement/tests, mais il pourrait peut-être sauter dans la production à tout moment. Donc, faire usage[DllImport(SetLastError = true)]
etMarshal.GetLastWin32Error()
et d'améliorer la qualité de votre sommeil 😉Oui, comme il est démontré dans DllImportAttribute.SetLastError Champ
Comme documenté dans Maréchal.GetLastWin32Error Méthode, si le cadre lui-même (par exemple, le garbage collector) appelle une méthode native qui définit une valeur d'erreur entre les appels à la méthode native et
GetLastError
vous obtenez la valeur de l'erreur de le cadre de l'appel à la place de votre appel.Puisque vous ne pouvez pas garantir que le cadre ne sera jamais appeler une méthode native entre votre appel et de l'appel à
GetLastError
, oui. Aussi, pourquoi pas?Il pourrait certainement être (par exemple, des changements dans le garbage collector), mais il n'a pas à.
GetLastError
. Voulez-vous vraiment à analyser la mise en œuvre de tous les appels que vous faites avant d'appelerGetLastError
?Marshal.GetLastWin32Error
est juste la même valeur sur un calque différent. Si le CLR est mal conçu et appelle d'autres WInApi fonctions, qui peut garantir que ce n'est pas l'écrasement de cette valeur par un autre appel d'unSetLastError = true
marqué l'importation ?Marshal.GetLastWin32Error
a été ajoutée spécifiquement pour le rendre encore possible de travailler avec des Api.Marshal.GetLastWin32Error
's planqué valeur. Ainsi, si vous effectuez un appel à un P/Invoke fonction avecSetLastError = true
, et vous n'avez pas d'autre P/Invoke appels sur le même thread (ce qui signifie aussi de ne pas appeler les fonctions de la bibliothèque qui pourraient eux-mêmes P/Invoke), puisMarshal.GetLastWin32Error
sera de retour la valeur deGetLastError
qu'il l'était au moment de votre appel de P/Invoke est de retour. Le monde géré pouvez contrôler quandMarshal.GetLastWin32Error
changements, mais pas quand laGetLastError
la fonction de l'API n'.