我写了一个类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
时第二次运行。
我的代码出了什么问题?
答案 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);
}