Set ListView排序箭头AccessViolationException错误

本文关键字:AccessViolationException 错误 ListView 排序 Set | 更新日期: 2023-09-27 18:26:06

我写了一个类MyListView,以便添加一个设置排序箭头的方法:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsDataTypes
{
    class MyListView : ListView
    {
        public const Int32 HDF_SORTDOWN = 0x0200;
        public const Int32 HDF_SORTUP = 0x0400;
        public const UInt32 HDI_FORMAT = 0x0004;
        public const UInt32 HDM_GETITEM = 0x120b;
        public const UInt32 HDM_SETITEM = 0x120c;
        public const UInt32 LVM_GETHEADER = 0x101f;
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 uMsg, UIntPtr wParam, IntPtr lParam);
        [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
        public static extern Int32 SetWindowTheme(IntPtr hWnd, String pszSubAppName, String pszSubIdList);
        struct HDITEM
        {
            public UInt32 mask;
            public Int32 cxy;
            public String pszText;
            public IntPtr hbm;
            public Int32 cchTextMax;
            public Int32 fmt;
            public IntPtr lParam;
            public Int32 iImage;
            public Int32 iOrder;
            public UInt32 type;
            public IntPtr pvFilter;
            public UInt32 state;
        }
        public MyListView()
        {
            this.DoubleBuffered = true;
        }
        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            SetWindowTheme(this.Handle, "Explorer", null);
        }
        public void SetSortArrow(int column, SortOrder sortOrder)
        {
            IntPtr hHeader = SendMessage(this.Handle, LVM_GETHEADER, UIntPtr.Zero, IntPtr.Zero);
            HDITEM headerItem = new HDITEM();
            headerItem.mask = HDI_FORMAT;
            IntPtr pHeaderItem = Marshal.AllocHGlobal(Marshal.SizeOf(headerItem));
            Marshal.StructureToPtr(headerItem, pHeaderItem, true);
            SendMessage(hHeader, HDM_GETITEM, new UIntPtr((UInt32)column), pHeaderItem);
            headerItem.fmt = ((HDITEM)Marshal.PtrToStructure(pHeaderItem, headerItem.GetType())).fmt;
            switch (sortOrder)
            {
                case SortOrder.Ascending:
                    headerItem.fmt &= ~HDF_SORTDOWN;
                    headerItem.fmt |= HDF_SORTUP;
                    break;
                case SortOrder.Descending:
                    headerItem.fmt &= ~HDF_SORTUP;
                    headerItem.fmt |= HDF_SORTDOWN;
                    break;
                case SortOrder.None:
                    headerItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
                    break;
            }
            Marshal.StructureToPtr(headerItem, pHeaderItem, true);
            SendMessage(hHeader, HDM_SETITEM, new UIntPtr((UInt32)column), pHeaderItem);
            Marshal.FreeHGlobal(pHeaderItem);
        }
    }
}

有时,即使我没有修改源代码,只是在调用SetSortArrow时第二次运行,也会发生错误。

我的代码出了什么问题?

Set ListView排序箭头AccessViolationException错误

        Marshal.StructureToPtr(headerItem, pHeaderItem, true);

最后一个参数是不正确的,很可能(但不能保证)会破坏你的程序。只有当pHeaderItem中的内存已经包含封送处理结构时,才应使用true,该结构需要在写入新结构之前释放。在您的情况下没有,分配的内存未初始化。当执法官试图释放pszText成员时,它会爆炸。当该成员的内存意外为零时,它不会爆炸,这并不罕见。

将false作为解决问题的最后一个参数。

互操作可能非常挑剔,所以尽可能少做通常是最好的。你做的远远超出了需要。同样的效果是在没有所有记忆杂耍的情况下实现的。

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 uMsg, IntPtr wParam, ref HDITEM lParam);
public void SetSortArrow(int column, SortOrder sortOrder)
{
    var pHeader = SendMessage(this.Handle, LVM_GETHEADER, UIntPtr.Zero, IntPtr.Zero);
    var pColumn = new IntPtr(column);
    var headerItem = new HDITEM {mask = HDI_FORMAT};
    SendMessage(pHeader, HDM_GETITEM, pColumn, ref headerItem);
    switch (sortOrder)
    {
        case SortOrder.Ascending:
            headerItem.fmt &= ~HDF_SORTDOWN;
            headerItem.fmt |= HDF_SORTUP;
            break;
        case SortOrder.Descending:
            headerItem.fmt &= ~HDF_SORTUP;
            headerItem.fmt |= HDF_SORTDOWN;
            break;
        case SortOrder.None:
            headerItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
            break;
    }
    SendMessage(pHeader, HDM_SETITEM, pColumn, ref headerItem);
}