提醒:本页面将不再更新、维护或者支持,文章、评论所叙述内容存在时效性,涉及技术细节或者软件使用方面不保证能够完全有效可操作,请谨慎参考!

最近在用C/C++开发一个小项目,文章好久没更新,跟大家打个招呼。今天要介绍的是Win32桌面程序开发的问题,当然我的博客关于Windows程序开发介绍得还是比较少的,主要因为我做Windows开发的时候由于博客不稳定,一直没有更新这类文章,当博客稳定下来后又主打脚本或者Web编程,所以相关内容比较少,以后慢慢增加丰富吧。

今天的问题是一个我遇到的程序Bug,我设计的某程序,其中用到了系统的文件图标,查阅Win32 API后得知可以使用 Shell_GetImageLists 这个API获取,比如说获取16*16大小的系统图标组可以如下编写:

HIMAGELIST himl;
Shell_GetImageLists(NULL, &himl);

其中 himl 就是包含系统图标组的ImageList,MSDN的介绍中强调了 不可以 使用ImageList_Destroy销毁这个ImageList:

Important The image lists retrieved through this function are global system image lists; do not call ImageList_Destroy using them.

好吧,我们就不Destroy这个ImageList,那么我要将其关联到ListView控件上,那么代码很简单,如下即可(其中hwndListView为ListView的句柄):

HIMAGELIST himl;
Shell_GetImageLists(NULL, &himl);
ListView_SetImageList(hwndListView, himl, LVSIL_SMALL);

似乎这样很完美了,也许今天的工作可以很好的收尾,但是事实却不是这样,自从加上上述代码后,程序变得极其不稳定,特别是当调用GetOpenFileName或者SHBrowseForFolder等Shell API时,程序会出现异常,要么闪退,要么是没有响应,于是我把注意力放在上面这三段代码上。依次注释排除Shell_GetImageLists和ListView_SetImageList这两个调用。

我特地查阅了 ListView_SetImageList API说明 (准确的说这不是API,只是个SendMessage的包装宏),出现了这么一段话:

The current image list will be destroyed when the list-view control is destroyed unless the LVS_SHAREIMAGELISTS style is set. If you use this message to replace one image list with another, your application must explicitly destroy all image lists other than the current one.

也就是说如果你不为ListView设置 LVS_SHAREIMAGELISTS 这个样式的话,当你销毁对话框时,指定的ListView也将被销毁,但是ListView在寿终正寝前会随手帮你做件“好事”——那就是Destroy关联其上的ImageList,好吧,这个才是问题的关键,当我为ListView设置了 LVS_SHAREIMAGELISTS 样式后问题才算解决。看来什么问题都不能想当然,最终代码如下:

HIMAGELIST himl;
Shell_GetImageLists(NULL, &himl);
SetWindowLongPtr(hwndListView, GWL_STYLE,
    LVS_SHAREIMAGELISTS | GetWindowLongPtr(hwndListView, GWL_STYLE));
ListView_SetImageList(hwndListView, himl, LVSIL_SMALL);