Bit*_*lue 45 c# winapi unmanaged managed marshalling
我测试了很多.但我发现那些2没有缺点!
但是看到接受的答案.
GetLastError托管代码是不安全的,因为框架可能在内部"覆盖"最后一个错误.我从来没有遇到任何明显的问题,GetLastError对我来说,.NET Framework足够智能,不会覆盖它.因此,我对该主题有几个问题:
[DllImport("kernel32.dll", SetLastError = true)]该SetLastError属性是否使Framework存储错误代码供使用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());
}
}
}
Run Code Online (Sandbox Code Playgroud)
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());
}
Run Code Online (Sandbox Code Playgroud)
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());
}
Run Code Online (Sandbox Code Playgroud)
// 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
Run Code Online (Sandbox Code Playgroud)
我没有看到任何区别!除了Marshal.GetLastWin32Error存储来自App-> CLR-> WinApi调用的结果之外,两者的行为相同,并且GetLastError仅存储来自App-> WinApi调用的结果.
Joc*_*ach 71
你必须经常使用Marshal.GetLastWin32Error.主要问题是垃圾收集器.如果它在调用SetVolumeLabel和调用之间运行,GetLastError那么你将收到错误的值,因为GC肯定会覆盖最后的结果.
因此,您始终需要SetLastError=true在DllImport-Attribute中指定:
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);
Run Code Online (Sandbox Code Playgroud)
这确保了marhsallling存根在本机函数"GetLastError"之后立即调用并将其存储在本地线程中.
如果您已指定此属性,则调用Marshal.GetLastWin32Error将始终具有正确的值.
有关更多信息,请参阅GetLastError和托管代码
.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());
}
}
}
Run Code Online (Sandbox Code Playgroud)
此外,这似乎取决于您正在使用的CLR!如果用.NET2编译它,它将产生"2/0"; 如果切换到.NET 4,它将输出"2/2"...
所以它取决于CLR版本,但你不应该信任本机GetLastError函数; 总是使用Marshal.GetLastWin32Error.
Bat*_*nit 10
[DllImport(SetLastError = true)]和Marshal.GetLastWin32Error()Marshal.GetLastWin32Error()在失败的Win32呼叫之后立即执行并在同一线程上执行.当我读到它时,Marshal.GetLastWin32Error可以在这里找到您需要的官方解释:
公共语言运行库可以对API进行内部调用,以覆盖操作系统维护的GetLastError.
换句话说:
在设置错误的Win32调用之间,CLR可能"插入"可能覆盖错误的其他Win32调用.指定[DllImport(SetLastError = true)]确保CLR在CLR执行任何意外的Win32调用之前检索错误代码.要访问我们需要使用的变量Marshal.GetLastWin32Error.
现在@Bitterblue发现这些"插入的呼叫"并不经常发生 - 他找不到任何东西.但这并不是真的令人惊讶.为什么?因为"黑匣子测试"是否能够GetLastError可靠地工作是非常困难的:
有一个特定的组件 - 垃圾收集器(GC) - 众所周知,如果存在内存压力并在该线程上进行一些处理(请参阅垃圾收集期间发生的情况),就会中断.net线程.现在,如果GC要执行失败的Win32呼叫,这将打破您的呼叫GetLastError.
总结一下,你有很多未知因素会影响其可靠性GetLastError.在开发/测试时,您很可能不会发现不可靠性问题,但随时可能会在生产中爆炸.所以要使用[DllImport(SetLastError = true)]并Marshal.GetLastWin32Error()改善你的睡眠质量;-)
在 [DllImport("kernel32.dll", SetLastError = true)] 中,SetLastError 属性是否使框架存储错误代码以供使用 Marshal.GetLastWin32Error() ?
是的,如DllImportAttribute.SetLastError 字段中所述
有没有一个例子,普通的 GetLastError 无法给出正确的结果?
如Marshal.GetLastWin32Error Method中所述,如果框架本身(例如垃圾收集器)调用任何在调用本机方法之间设置错误值的本机方法,GetLastError您将获得框架调用的错误值,而不是您的调用。
我真的必须使用 Marshal.GetLastWin32Error() 吗?
因为您无法确保框架永远不会在您的调用和对 的调用之间调用本机方法GetLastError,所以是的。另外,为什么不呢?
这个“问题”框架版本相关吗?
它绝对可能是(例如垃圾收集器中的更改),但并非必须如此。
| 归档时间: |
|
| 查看次数: |
51191 次 |
| 最近记录: |