I wrote a class MyListView in order to add an method to set sort arrow:
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);
}
}
}
Sometimes error happens even if I didn’t modify the source code and just run the second time when I call SetSortArrow.
What’s wrong with my code?
The last argument is incorrect and is likely (but not guaranteed) to bomb your program. You should only use true when the memory at pHeaderItem already contains a marshaled structure that needs to be released before writing the new one. There is none in your case, the allocated memory is uninitialized. It bombs when the marshaller tries to release the pszText member. It won’t bomb when the memory for that member is zero by accident, not entirely uncommon.
Pass false as the last argument to fix your problem.