设置ListView排序箭头AccessViolationException错误

时间:2012-02-28 14:11:36

标签: c# winforms listview pinvoke

我写了一个类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时第二次运行。

我的代码出了什么问题?

2 个答案:

答案 0 :(得分:1)

        Marshal.StructureToPtr(headerItem, pHeaderItem, true);

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

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

答案 1 :(得分:0)

Interop可能非常挑剔 - 所以尽量做到最好也是最好的。你所做的远不止于此。在没有所有记忆杂耍的情况下完成相同的效果。

[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);
}