Установить стрелку сортировки ListView Ошибка AccessViolationException - PullRequest
0 голосов
/ 28 февраля 2012

Я написал класс 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 ]

1 голос
/ 29 февраля 2012
        Marshal.StructureToPtr(headerItem, pHeaderItem, true);

Последний аргумент неверен и может (но не гарантировано) взорвать вашу программу.Вы должны использовать true только тогда, когда память в pHeaderItem уже содержит маршализованную структуру, которую необходимо освободить перед записью новой.В вашем случае их нет, выделенная память неинициализирована.Он взрывается, когда маршаллер пытается освободить член pszText.Он не будет бомбить, когда память этого члена случайно обнуляется, что не редкость.

Передайте false в качестве последнего аргумента для решения вашей проблемы.

0 голосов
/ 29 февраля 2012

Взаимодействие может быть очень привередливым, поэтому лучше всего делать как можно меньше.Вы делаете гораздо больше, чем нужно.Тот же эффект достигается без всякого манипулирования памятью.

[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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...