在每晚生成机上构建的应用程序无法在Windows Server 2012上运行,但在其他桌面上也可以正常运行。
那种异常(exception)
“试图读取或写入 protected 内存。这通常表明其他内存已损坏。”被抛出。
当我在WindowsServer2012计算机和构建计算机上使用远程调试进行调试时,我看到在代码中进行kernel32调用HeapSize的地方抛出了此异常。这是HeapSize
的导入和调用方式:
[DllImport("kernel32")]
static extern int HeapSize(int hHeap, int flags, void* block);
// Returns the size of a memory block.
public static int SizeOf(void* block)
{
int result = HeapSize(ph, 0, block);
if (result == -1) throw new InvalidOperationException();
return result;
}
这被称为不安全类的构造函数的一部分:
public UnManagedBuffer(StringBuilder sb)
{
PtrStart = (byte*)Marshal.StringToHGlobalAnsi(sb.ToString());
Size = UnManagedMemory.SizeOf(PtrStart);
PtrWriteNextValue = PtrStart + Size - 1;
PtrReturnNextValue = PtrStart;
}
关于什么可能丢失以及如何解决此问题的任何线索?
这是我在Windbg中看到的:
EventLog显示:
Log Name: Application
Source: .NET Runtime
Level: Error
Keywords: Classic
Description:
Application: TestEngine.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
at Core.Utils.UnManagedMemory.HeapSize(Int32, Int32, Void*)
at Core.Utils.UnManagedMemory.SizeOf(Void*)
at Core.Utils.UnManagedBuffer..ctor</Event>
Faulting application name: TestEngine.exe, version: 1.0.0.0, time stamp: 0x56b532bb
Faulting module name: ntdll.dll, version: 6.3.9600.18185, time stamp: 0x5683f0c5
Exception code: 0xc0000005
Fault offset: 0x0000000000057306
Faulting process id: 0x2eb8
Faulting application start time: 0x01d164e45b12d7dd
Faulting application path: C:\NGDLM\Lib\TestEngine.exe
Faulting module path: C:\Windows\SYSTEM32\ntdll.dll
Report Id: bea6eb89-d0d7-11e5-80eb-0050568cd888
Faulting package full name:
Faulting package-relative application ID:
最佳答案
您编写的代码永远都行不通。HeapSize
返回堆的大小,例如,通过调用HeapAlloc分配的内容。提供给HeapSize
的指针必须是通过调用HeapAlloc
返回的指针:
您正在调用HeapSize
,但是提供了一个可以在该堆中任何位置的指针。还是根本不在那个堆中:
PtrStart = (byte*)Marshal.StringToHGlobalAnsi(sb.ToString());
Size = UnManagedMemory.SizeOf(PtrStart);
PtrWriteNextValue = PtrStart + Size - 1;
PtrReturnNextValue = PtrStart;
Marshal.StringToHGlobalAnsi()
不仅会返回堆中某个地方的指针,而且不会返回指向堆本身的指针,您甚至不知道指针是从哪个堆中分配的,因为该进程可能分配了多个堆。所有这些都没有关系,因为您似乎对该功能的用途有基本的误解-您似乎正在使用它来检索在堆内进行的分配的大小。
Marshal.StringToHGlobalAnsi()
返回的内存不是通过调用HeapAlloc()
分配的(因为它不是堆!),而是通过调用AllocHGlobal分配的。由它分配的内存必须通过调用Marshal.FreeHGlobal()
释放:从
Marshal.StringToHGlobal()
的文档中:此
Marshal
方法与HeapAlloc
,HeapSize
或相关功能无关。如果您确实确实想找出
Marshal.StringToHGlobal()
返回的指针的内存分配大小,则可以深入研究source of the the Marshal class并发现它使用了win32函数 LocalAlloc
。碰巧LocalAlloc
有一个姊妹函数 LocalSize
,实际上可以用来查找分配的大小。但是,不能保证将来会这样做。因为.Net框架不能保证它将继续使用
LocalAlloc
。如果他们更改了内部结构,LocalSize
可能会停止工作。...
所有这些都说:
我不认为这是您最初打算做的任何事情
再次查看您的代码:
PtrStart = (byte*)Marshal.StringToHGlobalAnsi(sb.ToString());
Size = UnManagedMemory.SizeOf(PtrStart);
PtrWriteNextValue = PtrStart + Size - 1;
PtrReturnNextValue = PtrStart;
您正在尝试查找返回给您的ansi字符串的长度。
HeapSize
或LocalSize
的所有这些业务都是完全不相关的。如果只想查找“ansi”字符串的长度,则只需实现一个愚蠢的简单字符串长度,或者使用已经存在的任何实现。
以下程序使用
Marshal.StringToHGlobal()
并打印: public static void Main( string[] args )
{
IntPtr strPtr = IntPtr.Zero;
string str = "Hello";
try
{
strPtr = Marshal.StringToHGlobalAnsi( str );
Console.Out.WriteLine( "String: '{0}'; Length: {1}", str, AnsiStrLen( strPtr ) );
}
finally
{
if( strPtr != IntPtr.Zero )
{
Marshal.FreeHGlobal( strPtr );
}
}
}
public static int AnsiStrLen( IntPtr strPtr )
{
int size = 0;
while( Marshal.ReadByte( strPtr ) != 0 )
{
size++;
strPtr = IntPtr.Add( strPtr, 1 );
}
return size;
}