The following code fills TreeView1 component with the drives of the computer
in Windows XP SP2 successfully, but failed in Vista. What's wrong with the
code?
Thanks in advance!
void __fastcall TForm1::FirstTree(void)
{
LPMALLOC lpMalloc;
if (SUCCEEDED(SHGetMalloc(&lpMalloc))) {
LPSHELLFOLDER lpsf;
SHFILEINFO FileInfo;
unsigned int uSHFlags = SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME |
SHGFI_PIDL;
TTreeNode *TNode;
if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
LPITEMIDLIST lpidl;
if (SUCCEEDED(SHGetFolderLocation(Handle, CSIDL_DRIVES, NULL, 0,
&lpidl))) {
LPSHELLFOLDER lpsfSub;
if (SUCCEEDED(lpsf->BindToObject(lpidl, NULL, IID_IShellFolder,
reinterpret_cast<void **>(&lpsfSub)))) {
SHGetFileInfo((LPCSTR)lpidl, 0, &FileInfo, sizeof(SHFILEINFO),
uSHFlags);
FillTree(lpsfSub, lpidl);
lpsfSub->Release();
}
}
lpMalloc->Free(lpidl);
lpsf->Release();
}
lpMalloc->Release();
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FillTree (LPSHELLFOLDER lpsf, LPITEMIDLIST lpidl)
{
LPMALLOC lpMalloc;
Screen->Cursor = crHourGlass;
if (SUCCEEDED(SHGetMalloc(&lpMalloc))) {
LPENUMIDLIST lpeidl;
HRESULT HRes;
HRes = lpsf->EnumObjects(NULL, SHCONTF_NONFOLDERS, &lpeidl);
if (HRes == NOERROR) {
TreeView1->Items->BeginUpdate();
LPITEMIDLIST lpidlCurrent;
unsigned long ulFetched;
while (lpeidl->Next(1, &lpidlCurrent, &ulFetched) == S_OK) {
LPITEMIDLIST lpidlFQ = MergeIDLists (lpMalloc, lpidl, lpidlCurrent);
if (lpidlFQ) {
SHFILEINFO FileInfo;
DWORD FileAttrib;
unsigned int uSHFlags;
TTreeNode *Node;
uSHFlags = SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME | SHGFI_PIDL;
SHGetFileInfo(reinterpret_cast<LPCSTR>(lpidlFQ), FileAttrib,
&FileInfo, sizeof(SHFILEINFO), uSHFlags);
Node = TreeView1->Items->AddChild(NULL, FileInfo.szDisplayName);
Node->ImageIndex = FileInfo.iIcon;
Node->SelectedIndex = FileInfo.iIcon;
Node->Data = reinterpret_cast<void *>(lpidlFQ);
TTreeNode *ChildNode = TreeView1->Items->AddChild(Node, "");
ChildNode->ImageIndex = -1;
}
lpMalloc->Free(lpidlCurrent);
}
TreeView1->Items->EndUpdate();
lpeidl->Release();
}
else {
ShowMessage ("Error Code: 001");
}
lpMalloc->Release();
}
else {
Screen->Cursor = crArrow;
ShowMessage ("Error Code: 002");
}
Screen->Cursor = crArrow;
}
//---------------------------------------------------------------------------
>The following code fills TreeView1 component with the drives of the computer
>in Windows XP SP2 successfully, but failed in Vista. What's wrong with the
>code?
I would start by replacing the SUCCEEDED macro
with a function that reports what the error is.
> if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
BOOL SUCCEEDED( HRESULT code )
{
if( code < 0 )
{
//report error;
return FALSE;
}
return TRUE;
}
> The following code fills TreeView1 component with the drives of the
> computer in Windows XP SP2 successfully, but failed in Vista.
> What's wrong with the code?
You did not say where it is failing. Can you step through the code and find
the particular line that is not working?
Have you tried using Vista's new KNOWNFOLDERIDs instead of CSIDLs? In this
case, CSIDL_DRIVES maps to FOLDERID_ComputerFolder. Have a look at MSDN for
more details:
Known Folders
http://msdn.microsoft.com/en-us/library/bb776911(VS.85).aspx
Gambit
Thank you very much for your quick answer.
> You did not say where it is failing. Can you step through the code and
> find the particular line that is not working?
The code does not enter the while loop in function FillTree:
// while (lpeidl->Next(1, &lpidlCurrent, &ulFetched) == S_OK) {
void __fastcall TForm1::FirstTree(void)
{
LPMALLOC lpMalloc;
if (SUCCEEDED(SHGetMalloc(&lpMalloc))) {
LPSHELLFOLDER lpsf;
SHFILEINFO FileInfo;
unsigned int uSHFlags = SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME |
SHGFI_PIDL;
TTreeNode *TNode;
if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
LPITEMIDLIST lpidl;
if (SUCCEEDED(SHGetFolderLocation(Handle, CSIDL_DRIVES, NULL, 0,
&lpidl))) {
LPSHELLFOLDER lpsfSub;
if (SUCCEEDED(lpsf->BindToObject(lpidl, NULL, IID_IShellFolder,
reinterpret_cast<void **>(&lpsfSub)))) {
SHGetFileInfo((LPCSTR)lpidl, 0, &FileInfo, sizeof(SHFILEINFO),
uSHFlags);
FillTree(lpsfSub, lpidl);
lpsfSub->Release();
}
else ErrorFunc();
}
else ErrorFunc();
lpMalloc->Free(lpidl);
lpsf->Release();
}
else ErrorFunc();
lpMalloc->Release();
}
else ErrorFunc();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FillTree (LPSHELLFOLDER lpsf, LPITEMIDLIST lpidl)
{
LPMALLOC lpMalloc;
Screen->Cursor = crHourGlass;
if (SUCCEEDED(SHGetMalloc(&lpMalloc))) {
LPENUMIDLIST lpeidl;
HRESULT HRes;
HRes = lpsf->EnumObjects(NULL, SHCONTF_NONFOLDERS, &lpeidl);
if (HRes == NOERROR) {
TreeView1->Items->BeginUpdate();
LPITEMIDLIST lpidlCurrent;
unsigned long ulFetched;
while (lpeidl->Next(1, &lpidlCurrent, &ulFetched) == S_OK) { ////////
failing line
LPITEMIDLIST lpidlFQ = MergeIDLists (lpMalloc, lpidl, lpidlCurrent);
if (lpidlFQ) {
SHFILEINFO FileInfo;
DWORD FileAttrib;
unsigned int uSHFlags;
TTreeNode *Node;
uSHFlags = SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME | SHGFI_PIDL;
SHGetFileInfo(reinterpret_cast<LPCSTR>(lpidlFQ), FileAttrib,
&FileInfo, sizeof(SHFILEINFO), uSHFlags);
Node = TreeView1->Items->AddChild(NULL, FileInfo.szDisplayName);
Node->ImageIndex = FileInfo.iIcon;
Node->SelectedIndex = FileInfo.iIcon;
Node->Data = reinterpret_cast<void *>(lpidlFQ);
TTreeNode *ChildNode = TreeView1->Items->AddChild(Node, "");
ChildNode->ImageIndex = -1;
}
lpMalloc->Free(lpidlCurrent);
}
ErrorFunc();
TreeView1->Items->EndUpdate();
lpeidl->Release();
}
else {
ErrorFunc();
}
lpMalloc->Release();
}
else {
Screen->Cursor = crArrow;
ErrorFunc();
}
Screen->Cursor = crArrow;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ErrorFunc (void)
{
LPTSTR MsgBuf;
FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM, NULL,
GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &MsgBuf, 0, NULL);
MessageBox (NULL, MsgBuf, "Error Message", MB_OK|MB_ICONINFORMATION);
LocalFree (MsgBuf);
}
//-----------------------------------------------------------------------------
> Have you tried using Vista's new KNOWNFOLDERIDs instead of CSIDLs? In
> this case, CSIDL_DRIVES maps to FOLDERID_ComputerFolder. Have a look at
> MSDN for more details:
>
> Known Folders
> http://msdn.microsoft.com/en-us/library/bb776911(VS.85).aspx
Thanks for the tip. I think it requires Vista SDK. I have download and
installed it. I have begun studying on KNOWNFOLDERID.
> The code does not enter the while loop in function FillTree:
Is FillTree() being entered at all? If so, then the binding to CSIDL_DRIVES
is fine, but either SHGetMalloc() failed or the drives cannot be enumerated.
You need to more specific.
Gambit
I stepped through the code. FillTree() function is called.
SHGetMalloc() and EnumObjects() functions are successfully called in
FillTree().
The code only does not enter the while loop:
>SHGetMalloc() and EnumObjects() functions are successfully called in
>FillTree().
I suppose it might be the second case in this note:
http://msdn.microsoft.com/en-us/library/bb775066(VS.85).aspx
If the folder contains no suitable subobjects, then the IShellFolder::EnumObjects method
is permitted either to set *ppenumIDList to NULL and return S_FALSE, or to set
*ppenumIDList to an enumerator that produces no objects and return S_OK. Calling
applications must be prepared for both success cases.
> EnumObjects() functions are successfully called in FillTree().
If that were the case, then your while loop would be entered.
> The code only does not enter the while loop:
>
> while (lpeidl->Next(1, &lpidlCurrent, &ulFetched) == S_OK) {
Are you saying that Next() returns something other than S_OK? That would
mean the drives are not enumerable.
Gambit
Next() returns S_OK, but the while loop is not entered. It is really very
interesting.
Thanks for your answer.
> http://msdn.microsoft.com/en-us/library/bb775066(VS.85).aspx
> If the folder contains no suitable subobjects, then the
> IShellFolder::EnumObjects method
> is permitted either to set *ppenumIDList to NULL and return S_FALSE, or to
> set
> *ppenumIDList to an enumerator that produces no objects and return S_OK.
> Calling
> applications must be prepared for both success cases.
EnumObjects() returns S_OK.
> Next() returns S_OK
If EnumObjects() return S_OK and Next() return S_OK, then your while() loop
is guaranteed to be entered.
Gambit
Sorry. It was a big mistake. Next() return S_FALSE.
> Sorry. It was a big mistake. Next() return S_FALSE.
There you go then. That means the CSIDL_DRIVES enumerator is empty.
Gambit
Dear Remy and Bob,
Thanks both of you.
I have changed the line:
HRes = lpsf->EnumObjects(NULL, SHCONTF_NONFOLDERS, &lpeidl); // Works in XP
SP2
to:
HRes = lpsf->EnumObjects(Handle, SHCONTF_STORAGE, &lpeidl); // Works in
Vista
It works perfectly.