VB.NET 中的 CInt(Long) 在 32 位和 64 位环境中表现不同
今天,我在将 Long (Int64) 转换为 Integer (Int32) 时遇到问题。问题是我的代码始终在 32 位环境中工作,但是当我在 64 位计算机中尝试相同的可执行文件时,它会崩溃并出现 System.OverflowException 异常。
我已在 Visual Studio 2008 中的一个新项目中使用默认设置准备了此测试代码:
Module Module1
Sub Main()
Dim alpha As Long = -1
Dim delta As Integer
Try
delta = CInt(alpha And UInteger.MaxValue)
Console.WriteLine("CINT OK")
delta = Convert.ToInt32(alpha And UInteger.MaxValue)
Console.WriteLine("Convert.ToInt32 OK")
Catch ex As Exception
Console.WriteLine(ex.GetType().ToString())
Finally
Console.ReadLine()
End Try
End Sub
End Module
在我的 32 位设置(Windows XP SP3 32 位和 Windows 7 32 位)上,它打印到“CINT OK”,但在我测试过相同可执行文件的 64 位计算机(Windows 7 64 位)中,它仅打印异常名称。
这种行为有记录吗?我试图寻找参考资料,但惨遭失败。
作为参考,我也留下了 CIL 代码:
.method public static void Main() cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
// Code size 88 (0x58)
.maxstack 2
.locals init ([0] int64 alpha,
[1] int32 delta,
[2] class [mscorlib]System.Exception ex)
IL_0000: nop
IL_0001: ldc.i4.m1
IL_0002: conv.i8
IL_0003: stloc.0
IL_0004: nop
.try
{
.try
{
IL_0005: ldloc.0
IL_0006: ldc.i4.m1
IL_0007: conv.u8
IL_0008: and
IL_0009: conv.ovf.i4
IL_000a: stloc.1
IL_000b: ldstr "CINT OK"
IL_0010: call void [mscorlib]System.Console::WriteLine(string)
IL_0015: nop
IL_0016: ldloc.0
IL_0017: ldc.i4.m1
IL_0018: conv.u8
IL_0019: and
IL_001a: call int32 [mscorlib]System.Convert::ToInt32(int64)
IL_001f: stloc.1
IL_0020: ldstr "Convert.ToInt32 OK"
IL_0025: call void [mscorlib]System.Console::WriteLine(string)
IL_002a: nop
IL_002b: leave.s IL_0055
} // End .try
catch [mscorlib]System.Exception
{
IL_002d: dup
IL_002e: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception)
IL_0033: stloc.2
IL_0034: nop
IL_0035: ldloc.2
IL_0036: callvirt instance class [mscorlib]System.Type [mscorlib]System.Exception::GetType()
IL_003b: callvirt instance string [mscorlib]System.Type::ToString()
IL_0040: call void [mscorlib]System.Console::WriteLine(string)
IL_0045: nop
IL_0046: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError()
IL_004b: leave.s IL_0055
} // End handler
} // End .try
finally
{
IL_004d: nop
IL_004e: call string [mscorlib]System.Console::ReadLine()
IL_0053: pop
IL_0054: endfinally
} // End handler
IL_0055: nop
IL_0056: nop
IL_0057: ret
} // End of method Module1::Main
我怀疑行为不同的指令是conv.ovf.i4 或 ldc.i4.m1/conv.u8 对。
到底是怎么回事?
Convert.ToInt32(long)
在这两种环境中都会失败。只有 CInt(Long) 的行为有所不同。
Today I had a problem converting a Long (Int64) to an Integer (Int32). The problem is that my code was always working in 32-bit environments, but when I try THE SAME executable in a 64-bit computer it crashes with a System.OverflowException exception.
I've prepared this test code in Visual Studio 2008 in a new project with default settings:
Module Module1
Sub Main()
Dim alpha As Long = -1
Dim delta As Integer
Try
delta = CInt(alpha And UInteger.MaxValue)
Console.WriteLine("CINT OK")
delta = Convert.ToInt32(alpha And UInteger.MaxValue)
Console.WriteLine("Convert.ToInt32 OK")
Catch ex As Exception
Console.WriteLine(ex.GetType().ToString())
Finally
Console.ReadLine()
End Try
End Sub
End Module
On my 32-bit setups (Windows XP SP3 32-bit and Windows 7 32-bit) it prints up to "CINT OK", but in the 64-bit computer (Windows 7 64-bit) that I've tested THE SAME executable it prints the exception name only.
Is this behavior documented? I tried to find a reference, but I failed miserably.
For reference I leave the CIL code too:
.method public static void Main() cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
// Code size 88 (0x58)
.maxstack 2
.locals init ([0] int64 alpha,
[1] int32 delta,
[2] class [mscorlib]System.Exception ex)
IL_0000: nop
IL_0001: ldc.i4.m1
IL_0002: conv.i8
IL_0003: stloc.0
IL_0004: nop
.try
{
.try
{
IL_0005: ldloc.0
IL_0006: ldc.i4.m1
IL_0007: conv.u8
IL_0008: and
IL_0009: conv.ovf.i4
IL_000a: stloc.1
IL_000b: ldstr "CINT OK"
IL_0010: call void [mscorlib]System.Console::WriteLine(string)
IL_0015: nop
IL_0016: ldloc.0
IL_0017: ldc.i4.m1
IL_0018: conv.u8
IL_0019: and
IL_001a: call int32 [mscorlib]System.Convert::ToInt32(int64)
IL_001f: stloc.1
IL_0020: ldstr "Convert.ToInt32 OK"
IL_0025: call void [mscorlib]System.Console::WriteLine(string)
IL_002a: nop
IL_002b: leave.s IL_0055
} // End .try
catch [mscorlib]System.Exception
{
IL_002d: dup
IL_002e: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception)
IL_0033: stloc.2
IL_0034: nop
IL_0035: ldloc.2
IL_0036: callvirt instance class [mscorlib]System.Type [mscorlib]System.Exception::GetType()
IL_003b: callvirt instance string [mscorlib]System.Type::ToString()
IL_0040: call void [mscorlib]System.Console::WriteLine(string)
IL_0045: nop
IL_0046: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError()
IL_004b: leave.s IL_0055
} // End handler
} // End .try
finally
{
IL_004d: nop
IL_004e: call string [mscorlib]System.Console::ReadLine()
IL_0053: pop
IL_0054: endfinally
} // End handler
IL_0055: nop
IL_0056: nop
IL_0057: ret
} // End of method Module1::Main
I suspect that the instruction that is behaving differently is either conv.ovf.i4 or the ldc.i4.m1/conv.u8 pair.
What is going on?
Convert.ToInt32(long)
fails in both environments. It is only CInt(Long) which is behaving differently.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不幸的是,64 位版本是准确的。这确实是一个溢出,表达式的结果是一个 long,值为 &hffffffff。符号位与该值进行“与”运算,它不再是负值。结果值无法转换为整数,最大整数值为 &h7fffffff。您可以通过将以下代码添加到代码片段来看到这一点:
输出:4294967295
x64 抖动使用完全不同的方式来检查溢出,它不依赖于 CPU 溢出异常,而是显式地将值与 Integer.MaxValue 和 Integer 进行比较。最小值。 x86 抖动出错了,它对代码进行了太多优化,最终导致执行了一个不会引发 CPU 异常的无符号操作。
在 connect.microsoft.com 上提交错误报告可能不值得,解决 x86 抖动问题将是一个彻底的破坏性改变。你必须重新设计这个逻辑。不知道如何,我不明白你想做什么。
Unfortunately, the 64-bit version is accurate. It really is an overflow, the result of the expression is a long with the value &hffffffff. The sign bit is AND-ed off the value, it is no longer negative. The resulting value cannot be converted to an integer, the maximum integer value is &h7fffffff. You can see this by adding this code to your snippet:
Output: 4294967295
The x64 jitter uses an entirely different way to check for overflows, it doesn't rely on the CPU overflow exception but explicitly compares the values to Integer.MaxValue and Integer.MinValue. The x86 jitter gets it wrong, it optimizes the code too much and ends up making an unsigned operation that doesn't trip the CPU exception.
Filing a bug report at connect.microsoft.com is probably not worth the effort, fixing this for the x86 jitter would be a drastically breaking change. You'll have to rework this logic. Not sure how, I don't see what you are trying to do.
我不知道有任何真正的参考,但如果您访问此页面:
http://msdn.microsoft.com/en-us/library/system.int32.aspx
您可以在示例中看到他们使用
CInt
他们确实包装了它在OverflowException
处理程序中(尝试在该页面上搜索CInt
来找到它)。因此,至少他们隐含地表示,CInt
在某些情况下可以抛出该错误。如果您不希望引发异常,可以更改“高级编译选项”页面上的
删除整数溢出检查
设置。I don't know of any real reference as such, but if you go to this page:
http://msdn.microsoft.com/en-us/library/system.int32.aspx
You can see in the sample where they use
CInt
they do wrap it in aOverflowException
handler (try searching forCInt
on that page to find it). So at least they say implicitly thatCInt
can throw that in certain circumstances.If you do not want the exceptions being thrown you can change the
Remove integer overflow checks
setting on the Advanced Compile Options page.尝试将构建平台目标从“任何 CPU”更改为“x86”。
Try to change build platform target from “Any CPU” to "x86".
为了完成这个问题的文档,我做了这个:
使用 OllyDbg 我得到了这个:
正如你所看到的,第二个 CInt 句子比 AND 复杂得多(它实际上可以被抑制,因为 EBX 不会改变,并且 EFLAGS 是没有在任何地方消耗)。此问题的可能根源可以在 汉斯的回答
Just to complete the documentation of this issue I made this:
Using OllyDbg I got this:
As you can see the second CInt sentence is much more complex than just ANDing (which it could actually be suppressed as EBX won't change and the EFLAGS are not consumed anywhere). The probable origin of this problem can be seen in Hans' answer