VB.NET 中的 CInt(Long) 在 32 位和 64 位环境中表现不同

发布于 2024-08-31 05:58:31 字数 3254 浏览 6 评论 0原文

今天,我在将 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓝眸 2024-09-07 05:58:31

不幸的是,64 位版本是准确的。这确实是一个溢出,表达式的结果是一个 long,值为 &hffffffff。符号位与该值进行“与”运算,它不再是负值。结果值无法转换为整数,最大整数值为 &h7fffffff。您可以通过将以下代码添加到代码片段来看到这一点:

 Dim value As Long = alpha And UInteger.MaxValue
 Console.WriteLine(value)

输出: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:

 Dim value As Long = alpha And UInteger.MaxValue
 Console.WriteLine(value)

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.

但可醉心 2024-09-07 05:58:31

我不知道有任何真正的参考,但如果您访问此页面:

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 a OverflowException handler (try searching for CInt on that page to find it). So at least they say implicitly that CInt 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.

指尖微凉心微凉 2024-09-07 05:58:31

尝试将构建平台目标从“任何 CPU”更改为“x86”。

Try to change build platform target from “Any CPU” to "x86".

小猫一只 2024-09-07 05:58:31

为了完成这个问题的文档,我做了这个:

Imports System.Runtime.InteropServices

Module Module1
   <DllImport("KERNEL32.DLL", EntryPoint:="DebugBreak", _
        SetLastError:=False, CharSet:=CharSet.Unicode, _
        ExactSpelling:=True, _
        CallingConvention:=CallingConvention.StdCall)> _
        Public Sub DebugBreak()
   End Sub

   Sub Main()
      Dim alpha As Long = -1
      Dim delta As Integer

      DebugBreak() ' To call OllyDbg
      ' Needed to prevent the jitter from raising the overflow exception in the second CInt without really doing the convertion first
      alpha = alpha Xor Environment.TickCount
      Console.WriteLine(alpha)

      delta = CInt(alpha And UInteger.MaxValue)
      Console.WriteLine(delta)

      alpha = alpha And UInteger.MaxValue
      delta = CInt(alpha)
      Console.WriteLine(delta)

      Console.ReadLine()
   End Sub
End Module

使用 OllyDbg 我得到了这个:

CPU Disasm
Address   Hex dump          Command                                  Comments
00D10070    55              PUSH EBP
00D10071    8BEC            MOV EBP,ESP
00D10073    57              PUSH EDI
00D10074    56              PUSH ESI
00D10075    53              PUSH EBX
00D10076    E8 A1BFC7FF     CALL 0098C01C
00D1007B    E8 A18C1879     CALL <JMP.&KERNEL32.GetTickCount>        ; Jump to KERNEL32.GetTickCount
00D10080    99              CDQ
00D10081    F7D0            NOT EAX
00D10083    F7D2            NOT EDX
00D10085    8BF0            MOV ESI,EAX
00D10087    8BFA            MOV EDI,EDX
00D10089    E8 62D25D78     CALL 792ED2F0                            ; Called everytime Console is referenced here
00D1008E    57              PUSH EDI
00D1008F    56              PUSH ESI
00D10090    8BC8            MOV ECX,EAX
00D10092    8B01            MOV EAX,DWORD PTR DS:[ECX]
00D10094    FF90 C4000000   CALL DWORD PTR DS:[EAX+0C4]              ; Console.WriteLine(Int64)
00D1009A    8BDE            MOV EBX,ESI                              ; Note: EDI:ESI holds alpha variable
00D1009C    83E3 FF         AND EBX,FFFFFFFF                         ; delta = CInt(alpha And UInteger.MaxValue)
00D1009F    E8 4CD25D78     CALL 792ED2F0
00D100A4    8BC8            MOV ECX,EAX
00D100A6    8BD3            MOV EDX,EBX
00D100A8    8B01            MOV EAX,DWORD PTR DS:[ECX]
00D100AA    FF90 BC000000   CALL DWORD PTR DS:[EAX+0BC]              ; Console.WriteLine(Int32)
00D100B0    33FF            XOR EDI,EDI                              ; alpha = alpha And UInteger.MaxValue
00D100B2    85F6            TEST ESI,ESI                             ; delta = CInt(alpha) [Begins here]
00D100B4    7C 06           JL SHORT 00D100BC
00D100B6    85FF            TEST EDI,EDI
00D100B8    75 2B           JNE SHORT 00D100E5
00D100BA    EB 05           JMP SHORT 00D100C1
00D100BC    83FF FF         CMP EDI,-1
00D100BF    75 24           JNE SHORT 00D100E5
00D100C1    8BDE            MOV EBX,ESI                              ; delta = CInt(alpha) [Ends here]
00D100C3    E8 28D25D78     CALL 792ED2F0
00D100C8    8BC8            MOV ECX,EAX
00D100CA    8BD3            MOV EDX,EBX
00D100CC    8B01            MOV EAX,DWORD PTR DS:[ECX]
00D100CE    FF90 BC000000   CALL DWORD PTR DS:[EAX+0BC]              ; Console.WriteLine(Int32)
00D100D4    E8 1B1AA878     CALL 79791AF4
00D100D9    8BC8            MOV ECX,EAX
00D100DB    8B01            MOV EAX,DWORD PTR DS:[ECX]
00D100DD    FF50 64         CALL DWORD PTR DS:[EAX+64]
00D100E0    5B              POP EBX
00D100E1    5E              POP ESI
00D100E2    5F              POP EDI
00D100E3    5D              POP EBP
00D100E4    C3              RETN

正如你所看到的,第二个 CInt 句子比 AND 复杂得多(它实际上可以被抑制,因为 EBX 不会改变,并且 EFLAGS 是没有在任何地方消耗)。此问题的可能根源可以在 汉斯的回答

Just to complete the documentation of this issue I made this:

Imports System.Runtime.InteropServices

Module Module1
   <DllImport("KERNEL32.DLL", EntryPoint:="DebugBreak", _
        SetLastError:=False, CharSet:=CharSet.Unicode, _
        ExactSpelling:=True, _
        CallingConvention:=CallingConvention.StdCall)> _
        Public Sub DebugBreak()
   End Sub

   Sub Main()
      Dim alpha As Long = -1
      Dim delta As Integer

      DebugBreak() ' To call OllyDbg
      ' Needed to prevent the jitter from raising the overflow exception in the second CInt without really doing the convertion first
      alpha = alpha Xor Environment.TickCount
      Console.WriteLine(alpha)

      delta = CInt(alpha And UInteger.MaxValue)
      Console.WriteLine(delta)

      alpha = alpha And UInteger.MaxValue
      delta = CInt(alpha)
      Console.WriteLine(delta)

      Console.ReadLine()
   End Sub
End Module

Using OllyDbg I got this:

CPU Disasm
Address   Hex dump          Command                                  Comments
00D10070    55              PUSH EBP
00D10071    8BEC            MOV EBP,ESP
00D10073    57              PUSH EDI
00D10074    56              PUSH ESI
00D10075    53              PUSH EBX
00D10076    E8 A1BFC7FF     CALL 0098C01C
00D1007B    E8 A18C1879     CALL <JMP.&KERNEL32.GetTickCount>        ; Jump to KERNEL32.GetTickCount
00D10080    99              CDQ
00D10081    F7D0            NOT EAX
00D10083    F7D2            NOT EDX
00D10085    8BF0            MOV ESI,EAX
00D10087    8BFA            MOV EDI,EDX
00D10089    E8 62D25D78     CALL 792ED2F0                            ; Called everytime Console is referenced here
00D1008E    57              PUSH EDI
00D1008F    56              PUSH ESI
00D10090    8BC8            MOV ECX,EAX
00D10092    8B01            MOV EAX,DWORD PTR DS:[ECX]
00D10094    FF90 C4000000   CALL DWORD PTR DS:[EAX+0C4]              ; Console.WriteLine(Int64)
00D1009A    8BDE            MOV EBX,ESI                              ; Note: EDI:ESI holds alpha variable
00D1009C    83E3 FF         AND EBX,FFFFFFFF                         ; delta = CInt(alpha And UInteger.MaxValue)
00D1009F    E8 4CD25D78     CALL 792ED2F0
00D100A4    8BC8            MOV ECX,EAX
00D100A6    8BD3            MOV EDX,EBX
00D100A8    8B01            MOV EAX,DWORD PTR DS:[ECX]
00D100AA    FF90 BC000000   CALL DWORD PTR DS:[EAX+0BC]              ; Console.WriteLine(Int32)
00D100B0    33FF            XOR EDI,EDI                              ; alpha = alpha And UInteger.MaxValue
00D100B2    85F6            TEST ESI,ESI                             ; delta = CInt(alpha) [Begins here]
00D100B4    7C 06           JL SHORT 00D100BC
00D100B6    85FF            TEST EDI,EDI
00D100B8    75 2B           JNE SHORT 00D100E5
00D100BA    EB 05           JMP SHORT 00D100C1
00D100BC    83FF FF         CMP EDI,-1
00D100BF    75 24           JNE SHORT 00D100E5
00D100C1    8BDE            MOV EBX,ESI                              ; delta = CInt(alpha) [Ends here]
00D100C3    E8 28D25D78     CALL 792ED2F0
00D100C8    8BC8            MOV ECX,EAX
00D100CA    8BD3            MOV EDX,EBX
00D100CC    8B01            MOV EAX,DWORD PTR DS:[ECX]
00D100CE    FF90 BC000000   CALL DWORD PTR DS:[EAX+0BC]              ; Console.WriteLine(Int32)
00D100D4    E8 1B1AA878     CALL 79791AF4
00D100D9    8BC8            MOV ECX,EAX
00D100DB    8B01            MOV EAX,DWORD PTR DS:[ECX]
00D100DD    FF50 64         CALL DWORD PTR DS:[EAX+64]
00D100E0    5B              POP EBX
00D100E1    5E              POP ESI
00D100E2    5F              POP EDI
00D100E3    5D              POP EBP
00D100E4    C3              RETN

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文