我测试了很多。但我没有发现这两个的缺点!
但是请参阅已接受的答案。
我读了here在托管代码中调用
GetLastError
是不安全的,因为框架可能会在内部“覆盖”最后一个错误。 GetLastError
我从来没有遇到过任何明显的问题,而且在我看来,.NET Framework 足够聪明,不会覆盖它。因此,我有几个关于该主题的问题:
- 在
[DllImport("kernel32.dll", SetLastError = true)]
中SetLastError
属性是否使框架存储使用的错误代码编码(marshal).GetLastWin32Error()
? - 是否有普通
GetLastError
无法给出正确结果的示例? - 我真的需要使用
Marshal.GetLastWin32Error()
吗? - 这个“问题”与框架版本有关吗?
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());
}
}
}
产生错误:
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
我看不出有什么区别!两者的行为相同,除了 Marshal.GetLastWin32Error
也存储 App->CLR->WinApi 调用的结果,而 GetLastError
仅存储 App->WinApi 调用的结果。
Garbage Collection 似乎没有调用任何覆盖最后一个错误代码的 WinApi 函数
- GetLastError 是线程安全的。 SetLastError 为调用它的每个线程存储一个错误代码。
- GC 什么时候开始在我的线程中运行?
最佳答案
您必须始终使用 Marshal.GetLastWin32Error
.主要问题是垃圾收集器。如果它在调用 SetVolumeLabel
和调用 GetLastError
之间运行,那么您将收到错误的值,因为 GC 肯定已经覆盖了最后的结果。
因此您始终需要指定 SetLastError=true
在 DllImport
-属性中:
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);
这确保编码 stub 在 native 函数“GetLastError”之后立即调用并将其存储在本地线程中。
如果您指定了此属性,则对 Marshal.GetLastWin32Error
的调用将始终具有正确的值。
有关详细信息,另请参阅 "GetLastError and managed code"亚当·内森。
.NET 中的其他函数也可以更改窗口“GetLastError”。这是一个产生不同结果的示例:
using System.IO;
using System.Runtime.InteropServices;
public class ForceFailure
{
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);
public static void Main()
{
if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
System.Console.WriteLine("It worked???");
else
{
System.Console.WriteLine(Marshal.GetLastWin32Error());
try
{
using (new FileStream("sdsdafsdfsdfs sdsd ", FileMode.Open)) {}
}
catch
{
}
System.Console.WriteLine(GetLastError());
}
}
}
而且这似乎取决于您使用的 CLR!如果你用 .NET2 编译它,它会产生“2/0”;如果切换到 .NET 4,它将输出“2/2”...
所以它取决于 CLR 版本,但你不应该相信原生的 GetLastError
函数;始终使用 Marshal.GetLastWin32Error
。
关于c# - WinApi - GetLastError 与 Marshal.GetLastWin32Error,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17918266/