P
Paul W
Hello,
My problem concerns the shell. If there's a better forum for this post,
please let me know.
I'm trying to create a ListView control that displays the contents of a
folder with all the appropriate icons etc. I first tried this in c# with
reasonable success, but this is really a job for c++. However I've run into
a problem.
I have pretty much got the code down to do what I need it to do, but if I
access a folder with many items, problems begin. The controls within my
program and the entire windows environment start to not paint themselves
correctly. For example, icons show up in my ListView but no text...text
disappears from the column headers and from the menu, and if you keep going,
entire blocks of Window start paint themselves erraticly. I have a couple
of screen shots.
Here's the code that sets things up:
virtual void OnCreateControl()
{
__super::OnCreateControl();
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
UINT uFlags = SHGFI_SYSICONINDEX | SHGFI_ICON | SHGFI_USEFILEATTRIBUTES;
HIMAGELIST himl;
SHFILEINFO sfi;
HRESULT hr;
LPMALLOC pMalloc;
LPSHELLFOLDER pDesktopFolder = NULL;
LPSHELLFOLDER pParentFolder = NULL;
hr = SHGetDesktopFolder(&pDesktopFolder); // Desktop's IShellFolder
interface
hr = SHGetMalloc(&pMalloc); // IMalloc interface, for releasing PIDLs
if (SUCCEEDED(hr))
{
// Assisn the Desktop's IShellFolder interface and the IMalloc interface to
global variables
// Calling the above functions and passing in these global variables results
in a compiler
// error; error C2664: : cannot convert parameter 1 from 'LPSHELLFOLDER __gc
* ' to 'IShellFolder ** '
m_pDesktopFolder = pDesktopFolder;
m_pMalloc = pMalloc;
// Set the ListView's SmallImage list to the system's SmallImageList
himl =
(HIMAGELIST)SHGetFileInfo((LPCWSTR)Marshal::StringToCoTaskMemUni(Application
::StartupPath).ToPointer(), FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO),
uFlags | SHGFI_SMALLICON);
ListView_SetImageList((HWND)hWnd.ToPointer(), himl, LVSIL_SMALL);
// Set the ListView's LargeImage list to the system's LargeImageList
himl =
(HIMAGELIST)SHGetFileInfo((LPCWSTR)Marshal::StringToCoTaskMemUni(Application
::StartupPath).ToPointer(), FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO),
uFlags | SHGFI_LARGEICON);
ListView_SetImageList((HWND)hWnd.ToPointer(), himl, LVSIL_NORMAL);
} //if (SUCCEEDED(hr))
} //OnCreateControl
This is the code that enumerates a folder and stores the relevant info. I
wanted this to be simpler. Originally I only wanted to store the PIDL for
each item and use that to retrieve the info needed (icon index, display
text, file type etc.) but I found that when I did that the same problem also
happens, only sooner. The problem seems to be in the repeated calls to
SHGetFileInfo.
virtual int GetFiles(LPITEMIDLIST pidl)
{
int retVal = -1;
IEnumIDList* pEnumIDList = NULL;
HRESULT hr = NULL;
LPITEMIDLIST apidl = NULL;
LPITEMIDLIST fullPidl = NULL;
SHCONTF grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS |
SHCONTF_INCLUDEHIDDEN;
UINT uFlags = SHGFI_PIDL | SHGFI_ICON | SHGFI_USEFILEATTRIBUTES |
SHGFI_TYPENAME | SHGFI_DISPLAYNAME | SHGFI_OVERLAYINDEX;
SHFILEINFO sfi;
m_fileList = new ArrayList();
// m_pParentFolder is a global variable for the current folder's
IShellFolder interface
hr = m_pParentFolder->EnumObjects(NULL, grfFlags, &pEnumIDList);
if (SUCCEEDED(hr))
{
while (HRESULT_CODE(pEnumIDList->Next(1, &apidl, NULL)) == NOERROR)
{
// PIDL is a class with static methods for working with PIDLs, Append joins
two PIDLs into one,
// taking a relative PIDL and its parent, and making a fully qualified PIDL
// (code for Append is from the Microsoft help file)
fullPidl = PIDL::Append(m_currentFolderPidl, apidl);
hr = SHGetFileInfo((LPCWSTR)fullPidl, FILE_ATTRIBUTE_NORMAL, &sfi,
sizeof(SHFILEINFO), uFlags);
if (SUCCEEDED(hr))
{
// ShellItem is a class that holds the shell object's PIDL, name, type,
image index and image overlay index
// ShellItem's destructor frees the PIDL using the IMalloc interface.
m_fileList is an ArrayList of ShellItems.
m_fileList->Add(new ShellItem(apidl, sfi.szDisplayName, sfi.szTypeName,
sfi.iIcon & 0x00FFFFFF, sfi.iIcon >> 24));
} //if (SUCCEEDED(hr))
m_pMalloc->Free(fullPidl);
} //while (HRESULT_CODE(pEnumIDList->Next(1, &apidl, NULL)) == NOERROR)
pEnumIDList->Release();
m_fileList->Sort(NULL); // ShellItem implements the IComparable interface.
ShellItems are sorted by their PIDLs
retVal = m_fileList->Count;
} //if (SUCCEEDED(hr))
return retVal;
} //GetFiles
The base class is a virtual ListView meaning that the ListView doesn't store
any info about the items it contains. The client is responsible for that
and the ListView calls back the client when it needs to know how to display
an item. This is the code that gets called.
virtual void OnGetInfo(VirtualList::GetInfoEventArgs* e)
{
HRESULT hr = NULL;
ULONG rgfInOut = SFGAO_GHOSTED;
LPITEMIDLIST apidl =
static_cast<ShellItem*>(m_fileList->Item[e->Index])->ItemIdList;
e->ImageIndex =
static_cast<ShellItem*>(m_fileList->Item[e->Index])->ImageIndex;
e->OverlayIndex =
static_cast<ShellItem*>(m_fileList->Item[e->Index])->OverlayIndex;
// m_pParentFolder is a global variable for the current folder's
IShellFolder interface
hr = m_pParentFolder->GetAttributesOf(1, (LPCITEMIDLIST*)&apidl, &rgfInOut);
if (SUCCEEDED(hr))
{
e->Ghosted = (rgfInOut & SFGAO_GHOSTED) == SFGAO_GHOSTED;
} //if (SUCCEEDED(hr))
switch (e->Column)
{
case 0:
e->Text = static_cast<ShellItem*>(m_fileList->Item[e->Index])->Text;
break;
case 1:
e->Text = static_cast<ShellItem*>(m_fileList->Item[e->Index])->TypeName;
break;
} //switch (e->Column)
} //OnGetInfo
Thank you for reading this. Any help would be appreciated.
Paul
My problem concerns the shell. If there's a better forum for this post,
please let me know.
I'm trying to create a ListView control that displays the contents of a
folder with all the appropriate icons etc. I first tried this in c# with
reasonable success, but this is really a job for c++. However I've run into
a problem.
I have pretty much got the code down to do what I need it to do, but if I
access a folder with many items, problems begin. The controls within my
program and the entire windows environment start to not paint themselves
correctly. For example, icons show up in my ListView but no text...text
disappears from the column headers and from the menu, and if you keep going,
entire blocks of Window start paint themselves erraticly. I have a couple
of screen shots.
Here's the code that sets things up:
virtual void OnCreateControl()
{
__super::OnCreateControl();
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
UINT uFlags = SHGFI_SYSICONINDEX | SHGFI_ICON | SHGFI_USEFILEATTRIBUTES;
HIMAGELIST himl;
SHFILEINFO sfi;
HRESULT hr;
LPMALLOC pMalloc;
LPSHELLFOLDER pDesktopFolder = NULL;
LPSHELLFOLDER pParentFolder = NULL;
hr = SHGetDesktopFolder(&pDesktopFolder); // Desktop's IShellFolder
interface
hr = SHGetMalloc(&pMalloc); // IMalloc interface, for releasing PIDLs
if (SUCCEEDED(hr))
{
// Assisn the Desktop's IShellFolder interface and the IMalloc interface to
global variables
// Calling the above functions and passing in these global variables results
in a compiler
// error; error C2664: : cannot convert parameter 1 from 'LPSHELLFOLDER __gc
* ' to 'IShellFolder ** '
m_pDesktopFolder = pDesktopFolder;
m_pMalloc = pMalloc;
// Set the ListView's SmallImage list to the system's SmallImageList
himl =
(HIMAGELIST)SHGetFileInfo((LPCWSTR)Marshal::StringToCoTaskMemUni(Application
::StartupPath).ToPointer(), FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO),
uFlags | SHGFI_SMALLICON);
ListView_SetImageList((HWND)hWnd.ToPointer(), himl, LVSIL_SMALL);
// Set the ListView's LargeImage list to the system's LargeImageList
himl =
(HIMAGELIST)SHGetFileInfo((LPCWSTR)Marshal::StringToCoTaskMemUni(Application
::StartupPath).ToPointer(), FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO),
uFlags | SHGFI_LARGEICON);
ListView_SetImageList((HWND)hWnd.ToPointer(), himl, LVSIL_NORMAL);
} //if (SUCCEEDED(hr))
} //OnCreateControl
This is the code that enumerates a folder and stores the relevant info. I
wanted this to be simpler. Originally I only wanted to store the PIDL for
each item and use that to retrieve the info needed (icon index, display
text, file type etc.) but I found that when I did that the same problem also
happens, only sooner. The problem seems to be in the repeated calls to
SHGetFileInfo.
virtual int GetFiles(LPITEMIDLIST pidl)
{
int retVal = -1;
IEnumIDList* pEnumIDList = NULL;
HRESULT hr = NULL;
LPITEMIDLIST apidl = NULL;
LPITEMIDLIST fullPidl = NULL;
SHCONTF grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS |
SHCONTF_INCLUDEHIDDEN;
UINT uFlags = SHGFI_PIDL | SHGFI_ICON | SHGFI_USEFILEATTRIBUTES |
SHGFI_TYPENAME | SHGFI_DISPLAYNAME | SHGFI_OVERLAYINDEX;
SHFILEINFO sfi;
m_fileList = new ArrayList();
// m_pParentFolder is a global variable for the current folder's
IShellFolder interface
hr = m_pParentFolder->EnumObjects(NULL, grfFlags, &pEnumIDList);
if (SUCCEEDED(hr))
{
while (HRESULT_CODE(pEnumIDList->Next(1, &apidl, NULL)) == NOERROR)
{
// PIDL is a class with static methods for working with PIDLs, Append joins
two PIDLs into one,
// taking a relative PIDL and its parent, and making a fully qualified PIDL
// (code for Append is from the Microsoft help file)
fullPidl = PIDL::Append(m_currentFolderPidl, apidl);
hr = SHGetFileInfo((LPCWSTR)fullPidl, FILE_ATTRIBUTE_NORMAL, &sfi,
sizeof(SHFILEINFO), uFlags);
if (SUCCEEDED(hr))
{
// ShellItem is a class that holds the shell object's PIDL, name, type,
image index and image overlay index
// ShellItem's destructor frees the PIDL using the IMalloc interface.
m_fileList is an ArrayList of ShellItems.
m_fileList->Add(new ShellItem(apidl, sfi.szDisplayName, sfi.szTypeName,
sfi.iIcon & 0x00FFFFFF, sfi.iIcon >> 24));
} //if (SUCCEEDED(hr))
m_pMalloc->Free(fullPidl);
} //while (HRESULT_CODE(pEnumIDList->Next(1, &apidl, NULL)) == NOERROR)
pEnumIDList->Release();
m_fileList->Sort(NULL); // ShellItem implements the IComparable interface.
ShellItems are sorted by their PIDLs
retVal = m_fileList->Count;
} //if (SUCCEEDED(hr))
return retVal;
} //GetFiles
The base class is a virtual ListView meaning that the ListView doesn't store
any info about the items it contains. The client is responsible for that
and the ListView calls back the client when it needs to know how to display
an item. This is the code that gets called.
virtual void OnGetInfo(VirtualList::GetInfoEventArgs* e)
{
HRESULT hr = NULL;
ULONG rgfInOut = SFGAO_GHOSTED;
LPITEMIDLIST apidl =
static_cast<ShellItem*>(m_fileList->Item[e->Index])->ItemIdList;
e->ImageIndex =
static_cast<ShellItem*>(m_fileList->Item[e->Index])->ImageIndex;
e->OverlayIndex =
static_cast<ShellItem*>(m_fileList->Item[e->Index])->OverlayIndex;
// m_pParentFolder is a global variable for the current folder's
IShellFolder interface
hr = m_pParentFolder->GetAttributesOf(1, (LPCITEMIDLIST*)&apidl, &rgfInOut);
if (SUCCEEDED(hr))
{
e->Ghosted = (rgfInOut & SFGAO_GHOSTED) == SFGAO_GHOSTED;
} //if (SUCCEEDED(hr))
switch (e->Column)
{
case 0:
e->Text = static_cast<ShellItem*>(m_fileList->Item[e->Index])->Text;
break;
case 1:
e->Text = static_cast<ShellItem*>(m_fileList->Item[e->Index])->TypeName;
break;
} //switch (e->Column)
} //OnGetInfo
Thank you for reading this. Any help would be appreciated.
Paul