Windows 7上构建的二进制文件在Windows Server 2012上失败

NVK*_*NVK 5 c# winapi windows-7-x64 windows-server-2012

在夜间构建计算机上构建的应用程序在Windows Server 2012上不起作用,但在其他桌面上运行正常.

"尝试读取或写入受保护的内存"的例外情况.这通常表明其他内存已损坏.被扔了.

当我在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; 
}
Run Code Online (Sandbox Code Playgroud)

这被称为不安全类的构造函数的一部分:

    public UnManagedBuffer(StringBuilder sb)
    {
        PtrStart = (byte*)Marshal.StringToHGlobalAnsi(sb.ToString());
        Size = UnManagedMemory.SizeOf(PtrStart);
        PtrWriteNextValue = PtrStart + Size - 1;
        PtrReturnNextValue = PtrStart;
    }
Run Code Online (Sandbox Code Playgroud)

关于什么可能遗失的任何线索以及如何解决这个问题?

这就是我在Windbg中看到的:

这就是我在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: 
Run Code Online (Sandbox Code Playgroud)

ant*_*duh 2

您编写的代码不应该工作。

HeapSize返回堆的大小,例如,通过调用HeapAlloc分配的内容。提供给的指针HeapSize必须是通过调用返回的指针HeapAlloc

lpMem [输入]

指向函数将获取其大小的内存块的指针。这是由 HeapAlloc 或 HeapReAlloc 函数返回的指针。

您正在调用HeapSize,但提供了一个可以位于该堆内任何位置的指针;或者根本不在那个堆中:

    PtrStart = (byte*)Marshal.StringToHGlobalAnsi(sb.ToString());
    Size = UnManagedMemory.SizeOf(PtrStart);
    PtrWriteNextValue = PtrStart + Size - 1;
    PtrReturnNextValue = PtrStart;
Run Code Online (Sandbox Code Playgroud)

不仅会 Marshal.StringToHGlobalAnsi()返回堆中某处的指针,而不是指向堆本身的指针,您甚至不知道指针是从哪个堆分配的,因为进程可能分配了多个堆。

所有这些都不重要,因为您似乎对该函数的目的存在根本性误解 - 您似乎正在使用它来检索堆内分配的大小。返回的内存Marshal.StringToHGlobalAnsi()不是通过调用分配的HeapAlloc()(因为它不是堆!),而是通过调用AllocHGlobal分配的。它分配的内存必须通过调用来释放Marshal.FreeHGlobal()

来自Marshal.StringToHGlobal()的文档:

由于此方法分配字符串所需的非托管内存,因此始终通过调用 FreeHGlobal 释放内存。

该方法与,或相关功能Marshal无关。HeapAllocHeapSize

如果您确实想了解所返回的指针的内存分配大小Marshal.StringToHGlobal(),您可以深入研究Marshal 类的源代码并发现它使用了 win32 函数LocalAlloc。碰巧LocalAlloc有一个姊妹函数LocalSize,它确实可以用来查找分配的大小。

但是,不能保证这样做将来会起作用,因为 .Net 框架不保证它将继续使用LocalAlloc. 如果他们改变了内部结构LocalSize可能会停止工作。

...

所有这些都表明:

我认为这并不是你最初打算做的

再次查看您的代码:

    PtrStart = (byte*)Marshal.StringToHGlobalAnsi(sb.ToString());
    Size = UnManagedMemory.SizeOf(PtrStart);
    PtrWriteNextValue = PtrStart + Size - 1;
    PtrReturnNextValue = PtrStart;
Run Code Online (Sandbox Code Playgroud)

您正在尝试查找返回给您的 ansi 字符串的长度。

所有这些事情HeapSizeLocalSize完全无关。

如果您只想找到“ansi”字符串的长度,您只需要实现一个愚蠢的简单字符串长度,或者使用任何已有的实现。

以下程序使用Marshal.StringToHGlobal(), 并打印:

字符串:'你好';长度:5

    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;
    }
Run Code Online (Sandbox Code Playgroud)