C++ cli 什么情况促使.NET2.0字符串构造函数抛出异常?
我有一些代码,在使用C++ cli 什么情况促使.NET2.0字符串构造函数抛出异常?,c++-cli,.net-2.0,C++ Cli,.net 2.0,我有一些代码,在使用Stringcostructor时,有时(但不总是)抛出中描述的异常 本质上,我的代码如下所示(除了输入字符串数组的长度因输入而异): kb文章指出,这“可能”导致抛出异常,但我的单元测试和大多数情况下,它从未出现 我想知道是什么引发了异常,这样我就可以在单元测试中复制它,并验证它在我们的代码库中是永久固定的。他们在4.0中修复了它,但在2.0中仍然被破坏: using System; using System.Runtime.InteropServices; namesp
String
costructor时,有时(但不总是)抛出中描述的异常
本质上,我的代码如下所示(除了输入字符串数组的长度因输入而异):
kb文章指出,这“可能”导致抛出异常,但我的单元测试和大多数情况下,它从未出现
我想知道是什么引发了异常,这样我就可以在单元测试中复制它,并验证它在我们的代码库中是永久固定的。他们在4.0中修复了它,但在2.0中仍然被破坏:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication13
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
// For .NET 4.0
//[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
static unsafe void Main(string[] args)
{
IntPtr ptr = VirtualAlloc(
IntPtr.Zero,
(IntPtr)(4096 * 2),
0x1000 /* MEM_COMMIT */ | 0x2000 /* MEM_RESERVE */,
0x04 /* PAGE_READWRITE */);
IntPtr page1 = ptr;
IntPtr page2 = (IntPtr)((long)ptr + 4096);
uint oldAccess;
bool res = VirtualProtect(page2, 4096, 0x01 /* PAGE_NOACCESS */, out oldAccess);
try
{
Marshal.WriteByte(page1, 1);
Console.WriteLine("OK");
}
catch (AccessViolationException)
{
Console.WriteLine("KO");
}
try
{
Marshal.WriteByte(page2, 1);
Console.WriteLine("KO");
}
catch (AccessViolationException)
{
Console.WriteLine("OK");
}
try
{
byte b1 = Marshal.ReadByte(page1);
Console.WriteLine("OK");
}
catch (AccessViolationException)
{
Console.WriteLine("KO");
}
try
{
byte b2 = Marshal.ReadByte(page2);
Console.WriteLine("KO");
}
catch (AccessViolationException)
{
Console.WriteLine("OK");
}
for (int i = 0; i < 4096; i++)
{
Marshal.WriteByte(page1, i, (byte)'A');
}
sbyte* ptr2 = (sbyte*)page1;
try
{
var st1 = new string(ptr2, 0, 4096);
Console.WriteLine("OK");
}
catch (ArgumentOutOfRangeException)
{
Console.WriteLine("KO");
}
}
}
}
使用系统;
使用System.Runtime.InteropServices;
命名空间控制台应用程序13
{
班级计划
{
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部IntPtr VirtualAlloc(IntPtr lpAddress、IntPtr dwSize、uint flAllocationType、uint flProtect);
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部布尔虚拟保护(IntPtr lpAddress、uint dwSize、uint flNewProtect、out uint lpflOldProtect);
//对于.NET4.0
//[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
静态不安全void Main(字符串[]args)
{
IntPtr ptr=VirtualAlloc(
IntPtr.Zero,
(IntPtr)(4096*2),
0x1000/*MEM_COMMIT*/| 0x2000/*MEM_RESERVE*/,,
0x04/*页(读写*/);
IntPtr page1=ptr;
IntPtr第2页=(IntPtr)((长)ptr+4096);
uint-oldAccess;
bool res=VirtualProtect(第2页,4096页,0x01/*PAGE\u NOACCESS*/,out oldAccess);
尝试
{
Marshal.WriteByte(第1页,第1页);
控制台。写入线(“OK”);
}
捕获(AccessViolationException)
{
控制台。写入线(“KO”);
}
尝试
{
封送员写入字节(第2页,第1页);
控制台。写入线(“KO”);
}
捕获(AccessViolationException)
{
控制台。写入线(“OK”);
}
尝试
{
字节b1=封送。读取字节(第1页);
控制台。写入线(“OK”);
}
捕获(AccessViolationException)
{
控制台。写入线(“KO”);
}
尝试
{
字节b2=封送。读取字节(第2页);
控制台。写入线(“KO”);
}
捕获(AccessViolationException)
{
控制台。写入线(“OK”);
}
对于(int i=0;i<4096;i++)
{
Marshal.WriteByte(第1页,i,(字节)'A');
}
sbyte*ptr2=(sbyte*)第1页;
尝试
{
var st1=新字符串(ptr2,0,4096);
控制台。写入线(“OK”);
}
捕获(ArgumentOutOfRangeException)
{
控制台。写入线(“KO”);
}
}
}
}
您必须在.NET 4.0中取消注释一行。请注意,这段代码并没有释放它分配的内存,但这不是一个大问题,因为当进程结束时,操作系统会回收内存
这个程序做什么?它使用VirtualAlloc
分配8192字节(2页)。通过使用VirtualAlloc
这两个页面是页面对齐的。它禁用对第二页的访问(使用VirtualProtect
)。然后它用'A'
填充第一页。然后它尝试从第一页创建一个字符串。在.NET2.0上,string
构造函数尝试读取第二页的第一个字节(即使您告诉它字符串只有4096字节长)
在中间有一些测试,检查页面是否可以读/写
通常很难检查这种情况,因为很难在分配的可读内存空间的末尾有一个内存块。他们在4.0中修复了它,在2.0中仍然中断:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication13
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
// For .NET 4.0
//[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
static unsafe void Main(string[] args)
{
IntPtr ptr = VirtualAlloc(
IntPtr.Zero,
(IntPtr)(4096 * 2),
0x1000 /* MEM_COMMIT */ | 0x2000 /* MEM_RESERVE */,
0x04 /* PAGE_READWRITE */);
IntPtr page1 = ptr;
IntPtr page2 = (IntPtr)((long)ptr + 4096);
uint oldAccess;
bool res = VirtualProtect(page2, 4096, 0x01 /* PAGE_NOACCESS */, out oldAccess);
try
{
Marshal.WriteByte(page1, 1);
Console.WriteLine("OK");
}
catch (AccessViolationException)
{
Console.WriteLine("KO");
}
try
{
Marshal.WriteByte(page2, 1);
Console.WriteLine("KO");
}
catch (AccessViolationException)
{
Console.WriteLine("OK");
}
try
{
byte b1 = Marshal.ReadByte(page1);
Console.WriteLine("OK");
}
catch (AccessViolationException)
{
Console.WriteLine("KO");
}
try
{
byte b2 = Marshal.ReadByte(page2);
Console.WriteLine("KO");
}
catch (AccessViolationException)
{
Console.WriteLine("OK");
}
for (int i = 0; i < 4096; i++)
{
Marshal.WriteByte(page1, i, (byte)'A');
}
sbyte* ptr2 = (sbyte*)page1;
try
{
var st1 = new string(ptr2, 0, 4096);
Console.WriteLine("OK");
}
catch (ArgumentOutOfRangeException)
{
Console.WriteLine("KO");
}
}
}
}
使用系统;
使用System.Runtime.InteropServices;
命名空间控制台应用程序13
{
班级计划
{
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部IntPtr VirtualAlloc(IntPtr lpAddress、IntPtr dwSize、uint flAllocationType、uint flProtect);
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部布尔虚拟保护(IntPtr lpAddress、uint dwSize、uint flNewProtect、out uint lpflOldProtect);
//对于.NET4.0
//[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
静态不安全void Main(字符串[]args)
{
IntPtr ptr=VirtualAlloc(
IntPtr.Zero,
(IntPtr)(4096*2),
0x1000/*MEM_COMMIT*/| 0x2000/*MEM_RESERVE*/,,
0x04/*页(读写*/);
IntPtr page1=ptr;
IntPtr第2页=(IntPtr)((长)ptr+4096);
uint-oldAccess;
bool res=VirtualProtect(第2页,4096页,0x01/*PAGE\u NOACCESS*/,out oldAccess);
尝试
{
Marshal.WriteByte(第1页,第1页);
控制台。写入线(“OK”);
}
捕获(AccessViolationException)
{
控制台。写入线(“KO”);
}
尝试
{
封送员写入字节(第2页,第1页);
控制台。写入线(“KO”);
}
捕获(AccessViolationException)
{
控制台。写入线(“OK”);
}
尝试
{
字节b1=封送。读取字节(第1页);
控制台。写入线(“OK”);
}
捕获(AccessViolationException)
{
控制台。写入线(“KO”);
}
if( IsBadReadPtr(pszSource, (UINT_PTR)length + 1)) {
COMPlusThrowArgumentOutOfRange(L"ptr", L"ArgumentOutOfRange_PartialWCHAR");
}
LPVOID ptr = VirtualAlloc(0, 4096 * 2, 0x1000, 0x04); // ReadWrite
LPVOID page1 = ptr;
LPVOID page2 = (LPVOID)((long)ptr + 4096);
DWORD oldAccess;
bool res = VirtualProtect(page2, 4096, 0x01, &oldAccess);
char* ptr2 = (char*)page1;
String^ st1 = gcnew String(ptr2, 0, 4096); // <-- This will cause the exception.
Console::WriteLine(st1);