SysTools Logo SysTools


ListView_DeleteAllItems() and Windows 98


In the development process of the ChemBase (Softpedia, GitHub) was encountered a bug in the implementation of the items deletion for the ListView.

Since all of the software developed by SysTools targets "Windows All" (i.e. must work on the any Windows system x32/x64) there are compatability tests under Windows 98, Windows XP and Windows 10. If it works here - it should work anywhere between Windows 98 and Windows 10 inclusive.

One of the last performed tests for ChemBase was selecting every of 27000+ records from the database to the ListView (filled in just fine at around 5 seconds). But when ChemBase window was closed after that it hangs. That hang can be easy reproduce after selecting everything from the database and trying to close main window after that. Leaving ChemBase as it was and do not ending its task reveals that it can finally close only after around 10 minutes (Windows 98 in Microsoft Virtual PC). Inserting debug logs here and there didn't help because hang happened somewhere in the system core when destroying main application window. It's also was found out that the bug is reproduced and application hangs when performing any even empty result search after selecting everything from the database. Using logging in that area of code allow to pinpoint the culprit - it was a call to the ListView_DeleteAllItems() deleting all the items from the ListView.

The first assumption was that the Windows, probably, trying to redraw or update ListView after deleting each of its item and thus produce this long delay. However framing the delete function in code prohibits any redraw didn't help at all:

EnableWindow(wnd, FALSE);
ShowWindow(wnd, SW_HIDE);
SendMessage(wnd, WM_SETREDRAW, FALSE, 0);

ListView_DeleteAllItems(wnd);

SendMessage(wnd, WM_SETREDRAW, TRUE, 0);
ShowWindow(wnd, SW_SHOW);
EnableWindow(wnd, TRUE);

The second assumption was to completely drop ListView_DeleteAllItems() and delete all the items manually:

while (ListView_DeleteItem(wnd, 0));

ListView_DeleteItem() required item index for deletion and it's easy to use 0 here because it's always available while ListView didn't empty. Surprisingly this attempt has taken the same 10 minutes.

The last tested assumption was to manually delete items by it's index in a reverse order - i.e. from the last one to first one:

for (i = ListView_GetItemCount(wnd); i > 0; i--) {
  ListView_DeleteItem(wnd, i - 1);
}

And only this code finally takes just 15 seconds instead of 10 minutes.

Probably in Windows 98 (and maybe in some older Windows systems too) deleting all items via ListView_DeleteAllItems() was done through sequental deletion for the first zero item since it was always exists for non-empty ListView. It should be noted that ListView_DeleteAllItems() in Windows XP works instantly - i.e. starting at least from that operation system deletion was fixed.

It's easy to explain this delay in deletion - probably after deleting each item all followed items was shifted and/or memory was reallocated.

In the end code for deleting all the items was looking something like this:

/*
  PATCH: Windows 98 can add 27k+ items just fine (around 10 seconds)
  but deleting with ListView_DeleteAllItems() can take up to whole 10 minutes!
  items MUST be deleted from last to first (around 15 seconds) and not vice versa
  or it takes the same 10 minutes (the Microsoft way)
  this bug was fixed at least in Windows XP and newer
*/

void ListView_ManualDeleteAllItems(HWND wnd) {
int i;
  for (i = ListView_GetItemCount(wnd); i > 0; i--) {
    ListView_DeleteItem(wnd, i - 1);
  }
  // just in case
  ListView_DeleteAllItems(wnd);
}

And it's important to not forget add a call to this routine before main window was closing or destroying, so the system default handler didn't work. Or rather system default handler will be called anyway when destroying ListView, but it will be empty at that time.

The disadvantages of this deletion method is the fact that it works a bit slow on any Windows (Windows 98: 15 seconds, Windows XP: 3 seconds), in contrast to ListView_DeleteAllItems() which work instantly on Windows XP and newer. However compared to the 10 minutes this slowdown was neglible.


2017.03.05


[ Статьи ]