简体   繁体   中英

AccessViolationException in .NET framework 4.5 or higher

I'm trying to run a .NET C# console app found on internet ( article , code ).

It uses a few pInvoke calls, and at some point triggers Marshal.PtrToStringUni(IntPtr ptr, int len) .

What is very strange, and that I can't fix, is this:

  • If I compile the project to .NET Framework 4.0, it works.
  • Under .NET framework 4.5 or higher, it throws a AccessViolationException (the infamous "Attempted to read or write protected memory. This is often an indication that other memory is corrupt.")

The stack trace tells me:

at System.Buffer.Memmove(Byte* dest, Byte* src, UInt64 len) at System.String.CtorCharPtrStartLength(Char* ptr, Int32 startIndex, Int32 length) at System.Runtime.InteropServices.Marshal.PtrToStringUni(IntPtr ptr, Int32 len)

I've searched the net for hours, and saw similar problems, but nothing that could help me. I absolutely need to implement this functionality in my project, and feel that to compile this part in .Net Framework 4.0 while the rest is in higher version could open to some troubles in the future. Anyway, there has to be a way to make it work.

Is there a way to make this code work?

EDIT : I just updated the link 'code'. The author just commited a new version.

EDIT 2: I understand that not having code directly here doesn't makes one want to help. I thought that it would be more efficient to link to the article that list the important parts.

So here is some context and some code:

The code aims to list all the files locked by a process. It first gets a list of SYSTEM_HANDLE_INFORMATION. Here is the struct:

 [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct SYSTEM_HANDLE_INFORMATION
    { 
        public int ProcessID;
        public byte ObjectTypeNumber;
        public byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
        public ushort Handle;
        public int Object_Pointer;
        public UInt32 GrantedAccess;
    }

It iterates through the collection, and sends each item in the GetFileDetails Method.

Here is the method, I only copy it until the try/catch statement that fails in .NET 4.5, and succeeds in .NET 4.0:

private static FileDetails GetFileDetails(Win32API.SYSTEM_HANDLE_INFORMATION sYSTEM_HANDLE_INFORMATION)
    {
        FileDetails fd = new FileDetails();
        fd.Name = "";
        IntPtr ipHandle = IntPtr.Zero;
        Win32API.OBJECT_BASIC_INFORMATION objBasic = new Win32API.OBJECT_BASIC_INFORMATION();
        IntPtr ipBasic = IntPtr.Zero;
        Win32API.OBJECT_TYPE_INFORMATION objObjectType = new Win32API.OBJECT_TYPE_INFORMATION();
        IntPtr ipObjectType = IntPtr.Zero;
        Win32API.OBJECT_NAME_INFORMATION objObjectName = new Win32API.OBJECT_NAME_INFORMATION();
        IntPtr ipObjectName = IntPtr.Zero;
        string strObjectTypeName = "";
        string strObjectName = "";
        int nLength = 0;
        int nReturn = 0;
        IntPtr ipTemp = IntPtr.Zero;


        if (!Win32API.DuplicateHandle(m_ipProcessHwnd, sYSTEM_HANDLE_INFORMATION.Handle,Win32API.GetCurrentProcess(), out ipHandle, 0, false, Win32API.DUPLICATE_SAME_ACCESS)) return fd;

        ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
        Win32API.NtQueryObject(ipHandle, (int) Win32API.ObjectInformationClass.ObjectBasicInformation, ipBasic, Marshal.SizeOf(objBasic), ref nLength);
        objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
        Marshal.FreeHGlobal(ipBasic);


        ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
        nLength = objBasic.TypeInformationLength;
        while ((uint)(nReturn = Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectTypeInformation, ipObjectType, nLength, ref nLength)) == Win32API.STATUS_INFO_LENGTH_MISMATCH)
        {
            Marshal.FreeHGlobal(ipObjectType);
            ipObjectType = Marshal.AllocHGlobal(nLength);
        }

        objObjectType = (Win32API.OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType());
        if (Is64Bits())
        {
            ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32);             
        }
        else
        {
            ipTemp = objObjectType.Name.Buffer;          
        }

        try
        {
            strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
        }
        catch (AccessViolationException)
        {
            return null;
        }

For those interested, the full code is available in the link above Thank you for your help

Access Violation excpetion come from the OS because of some faulty pointers. As .NET is designed to avoid naked pointers, there is only a few possible sources:

  • Calling Unamanaged Code via P/Invoke or COM interop.
  • Binarity missmatch. If the unmanaged code you call is x32 and you call it from x64 (or vice versa), it will not work. The way around that is to either hardcode the binarity or use a helper process of proper binarity. The linked code is said to exlcusively run for x64. If you do not define binarity, the Framework can pick "whatever" binarity it feels like today. Have you picked the Target of the Project accordingly?
  • 3rd parter interferece. It was a few years back, but we had a case where a certain Virus scanner would kill every other Windows Forms application. Try excludin the programm/turning background protection off for a spell.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM