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
时第二次运行,也会发生错误。
我的代码出了什么问题?
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);
}