CInt (Long) in VB.NET anders verhalten in 32- und 64-Bit-Umgebungen
-
03-10-2019 - |
Frage
Heute hatte ich ein Problem, ein Long (Int64) zu einem Integer (Int32) umgewandelt wird. Das Problem ist, dass mein Code immer in 32-Bit-Umgebungen arbeiten, aber wenn ich versuche, GLEICH ausführbare Datei in einem 64-Bit-Computern stürzt mit einer System.OverflowException Ausnahme.
Ich habe diesen Test-Code vorbereitet in Visual Studio 2008 in einem neuen Projekt mit den Standardeinstellung:
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
Auf meinen 32-Bit-Setups (Windows XP SP3 32-Bit und Windows 7 32-Bit) druckt ihn bis zu "CINT OK", aber in den 64-Bit-Computern (Windows 7 64-Bit), dass ich getestet GLEICH ausführbare Datei druckt es die Ausnahme Namen nur.
Ist dieses Verhalten dokumentiert? Ich habe versucht, einen Verweis zu finden, aber ich kläglich gescheitert.
Als Referenz lasse ich die CIL Code zu:
.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
Ich vermute, dass die Anweisung, die anders verhält entweder conv.ovf.i4 ist oder das ldc.i4.m1 / conv.u8 Paar.
Was ist da los?
versagt Convert.ToInt32(long)
in beiden Umgebungen. Es ist nur CInt (Long), die anders verhält.
Lösung
Leider ist die 64-Bit-Version korrekt. Es ist wirklich ein Überlauf ist, ist das Ergebnis des Ausdrucks ein lange mit dem Wert & HFFFFFFFF. Das Vorzeichenbit ist AND-ed aus dem Wert, es ist nicht mehr negativ. Der resultierende Wert kann nicht in eine ganze Zahl umgewandelt werden, die maximale ganze Zahl ist, & h7fffffff. Sie können, indem Sie diesen Code in Ihre Schnipsel sehen folgendermaßen aus:
Dim value As Long = alpha And UInteger.MaxValue
Console.WriteLine(value)
Ausgabe: 4294967295
Der x64-Jitter verwendet eine völlig andere Art und Weise für Überläufe zu überprüfen, es beruht nicht auf der CPU-Überlauf Ausnahme, sondern vergleicht explizit die Werte zu Integer.MaxValue und Integer.MinValue. Der x86-Jitter wird es falsch, den Code zu viel und Enden optimiert einen unsignierten Betrieb bis zu machen, die nicht die CPU Ausnahme nicht auslöst.
bei connect.microsoft.com einen Fehlerbericht Filing ist wahrscheinlich nicht die Mühe wert, Befestigungs dies für den x86-Jitter eine drastisch zu brechen Änderung wäre. Sie finden diese Logik überarbeiten müssen. Nicht sicher, wie ich nicht sehen, was Sie zu tun versuchen.
Andere Tipps
Ich weiß nicht, von einer echten Referenz als solche, aber wenn Sie zu dieser Seite:
http://msdn.microsoft.com/en-us /library/system.int32.aspx
Sie können in der Probe sehen, wo sie CInt
verwenden sie es tun wickeln in einem OverflowException
Handler (versuchen, für CInt
Suche auf dieser Seite zu finden). So zumindest sagen, dass sie implizit, dass CInt
dass unter bestimmten Umständen werfen kann.
Wenn Sie nicht möchten, dass die Ausnahmen geworfen werden Sie die Remove integer overflow checks
Einstellung auf dem Advanced Kompilieroptionen Seite ändern können.
Versuchen Sie Änderung Bauplattform Ziel von „Any CPU“ auf „x86“.
Sie einfach die Dokumentation dieser Ausgabe abzuschließen ich das gemacht:
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
Mit OllyDbg Ich habe diese:
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
Wenn Sie den zweiten Satz CInt sehen kann, ist viel komplexer als nur Anding (was es tatsächlich als EBX unterdrückt werden könnten, werden nicht geändert, und die EFLAGS sind nicht überall verbraucht). Der wahrscheinliche Ursprung dieses Problems kann in Hans' Antwort