Problem description:
https://github.com/dotnet/winforms/pull/2654 introduced tests for ListViewGroup.Footer that are failing on CI agents with the following stack:
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at System.Windows.Forms.NativeWindow.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.ListView.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.NativeWindow.Callback(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW[[Interop+ComCtl32+LVGROUPW, System.Windows.Forms.Primitives, Version=42.42.42.42, Culture=neutral, PublicKeyToken=b77a5c561934e089]](IntPtr, WM, IntPtr, LVGROUPW ByRef)
at System.Windows.Forms.Tests.ListViewTests+<>c.<ListView_Handle_GetWithGroups_Success>b__62_0()
at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean, Boolean)
at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
at System.Reflection.MethodBase.Invoke(System.Object, System.Object[])
at Microsoft.DotNet.RemoteExecutor.Program.Main(System.String[])
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at System.Windows.Forms.NativeWindow.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.ListView.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.NativeWindow.Callback(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW[[Interop+ComCtl32+LVGROUPW, System.Windows.Forms.Primitives, Version=42.42.42.42, Culture=neutral, PublicKeyToken=b77a5c561934e089]](IntPtr, WM, IntPtr, LVGROUPW ByRef)
at System.Windows.Forms.Tests.ListViewGroupTests+<>c.<ListViewGroup_Footer_GetGroupInfo_Success>b__10_0()
at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean, Boolean)
at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
at System.Reflection.MethodBase.Invoke(System.Object, System.Object[])
at Microsoft.DotNet.RemoteExecutor.Program.Main(System.String[])
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at System.Windows.Forms.NativeWindow.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.ListView.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.NativeWindow.Callback(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW[[Interop+ComCtl32+LVGROUPW, System.Windows.Forms.Primitives, Version=42.42.42.42, Culture=neutral, PublicKeyToken=b77a5c561934e089]](IntPtr, WM, IntPtr, LVGROUPW ByRef)
at System.Windows.Forms.Tests.ListViewGroupTests+<>c.<ListView_FooterAlignment_GetGroupInfo_Success>b__16_0(System.String, System.String, System.String)
at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean, Boolean)
at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
at System.Reflection.MethodBase.Invoke(System.Object, System.Object[])
at Microsoft.DotNet.RemoteExecutor.Program.Main(System.String[])
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at System.Windows.Forms.NativeWindow.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.ListView.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.NativeWindow.Callback(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW[[Interop+ComCtl32+LVGROUPW, System.Windows.Forms.Primitives, Version=42.42.42.42, Culture=neutral, PublicKeyToken=b77a5c561934e089]](IntPtr, WM, IntPtr, LVGROUPW ByRef)
at System.Windows.Forms.Tests.ListViewGroupTests+<>c.<ListView_FooterAlignment_GetGroupInfo_Success>b__16_0(System.String, System.String, System.String)
at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean, Boolean)
at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
at System.Reflection.MethodBase.Invoke(System.Object, System.Object[])
at Microsoft.DotNet.RemoteExecutor.Program.Main(System.String[])
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at System.Windows.Forms.NativeWindow.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.ListView.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.NativeWindow.Callback(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW[[Interop+ComCtl32+LVGROUPW, System.Windows.Forms.Primitives, Version=42.42.42.42, Culture=neutral, PublicKeyToken=b77a5c561934e089]](IntPtr, WM, IntPtr, LVGROUPW ByRef)
at System.Windows.Forms.Tests.ListViewGroupTests+<>c.<ListView_FooterAlignment_GetGroupInfo_Success>b__16_0(System.String, System.String, System.String)
at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean, Boolean)
at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
at System.Reflection.MethodBase.Invoke(System.Object, System.Object[])
at Microsoft.DotNet.RemoteExecutor.Program.Main(System.String[])
Expected behavior:
ListView_FooterAlignment_GetGroupInfo_Success tests pass/cc: @hughbe
After a lot of back and forth I managed to narrow it down to the following test input:
yield return new object[] { string.Empty, HorizontalAlignment.Left, 0x00000001 };
yield return new object[] { string.Empty, HorizontalAlignment.Center, 0x00000001 };
yield return new object[] { string.Empty, HorizontalAlignment.Right, 0x00000001 };
If I disable this input, tests pass. Otherwise the tests a failing with the following:
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.CallWindowProcW(IntPtr, IntPtr, WM, IntPtr, IntPtr)
at System.Windows.Forms.NativeWindow.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.DefWndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.ListView.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
at System.Windows.Forms.NativeWindow.Callback(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW(IntPtr, WM, IntPtr, IntPtr)
at Interop+User32.SendMessageW[[Interop+ComCtl32+LVGROUPW, System.Windows.Forms.Primitives, Version=42.42.42.42, Culture=neutral, PublicKeyToken=b77a5c561934e089]](IntPtr, WM, IntPtr, LVGROUPW ByRef)
at System.Windows.Forms.Tests.ListViewGroupTests+<>c.<ListView_FooterAlignment_GetGroupInfo_Success>b__19_0(System.String, System.String, System.String)
at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean, Boolean)
at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
at System.Reflection.MethodBase.Invoke(System.Object, System.Object[])
at Microsoft.DotNet.RemoteExecutor.Program.Main(System.String[])
System.Windows.Forms.Tests.ListViewGroupTests.ListView_FooterAlignment_GetGroupInfo_Success(footerParam: "", valueParam: Left, expectedAlignParam: 1) [FAIL]
Assert.Equal() Failure
Expected: 0
Actual: -1073741819
Stack Trace:
C:\Development\winforms.temp\src\System.Windows.Forms\tests\UnitTests\System\Windows\Forms\ListViewGroupTests.cs(372,0): at System.Windows.Forms.Tests.ListViewGroupTests.ListView_FooterAlignment_GetGroupInfo_Success(String footerParam, HorizontalAlignment valueParam, Int32 expectedAlignParam)
Output:
ListView_FooterAlignment_GetGroupInfo_Success: footerParam: '', valueParam: Left, expectedAlignParam: 1
Given that it fails in the RemoteExecutor process I wonder if its possible to take the test code and repro (and debug) in a standalone application? It seems RemoteExecutor isn't STA and the test isn't setting up a proper application message loop either, so maybe we are seeing a loophole where WinForms (or rather one of its referenced native components) doesn't get properly initialized.
[edit] yes putting it in a standalone program repros the crash and allows debugging - no the problem is not related to STA or other process initialization, still crashes when putting the code into an otherwise normal WinForms sample application. x86 vs x64 makes no difference, so no sign extension error either.
Yep, I'm slowly getting there.... First we need https://github.com/dotnet/winforms/pull/2988 as a base, so we know when remotely executed tests fail.
I'll pick up this tomorrow
It may be related to the fact that SendGroupMessage always sets LVGF.FOOTER but only conditionally sets the footer text lvgroup.pszFooter. This would leave the footer pointer NULL when you try to set it to an empty string.
Native code crashes in
comctl32.dll!StringCchCopyW(unsigned short *,unsigned int,unsigned short const *)
comctl32.dll!CListGroup::GetGroupInfo(int,struct tagLVGROUP *)
comctl32.dll!CLVGroupManager::OnGetGroupInfoByIndex(int,struct tagLVGROUP *)
comctl32.dll!CListView::WndProc()
Which indicates that something is wrong with copying the footer text. Unfortunately a naive attempt to fix this by removing the LVGF.FOOTER flag on empty strings didn't change anything, so not sure who is wrong here. Might be a bug in native code when you try to read the footer without ever setting it?
Unconditionally setting the footer (like its done with the header) works but I'm not sure if that has other negative side effects (I mean there must be a reason why it was done conditionally in the first place? or was this just trying to not call new code paths when the new feature isn't used?)
First we need #2988 as a base, so we know when remotely executed tests fail.
makes sense
I've done a bit of thinking, and maybe we should set pszFooter to '\0' if Footer is empty (rather than NULL) - see https://github.com/dotnet/winforms/pull/2991.
The reason the initial implementation always has the LVGF.FOOTER flag is because we need to be able to hide the footer if we set Footer to empty/null after setting it to a non-empty/null value.
I agree it seems like the native implementation is not very robust/reliant against null pointers etc. This could be something to bring up with the comctl team