//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1995, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementations of the following Win95 Shell Classes:
/// TPidl, TShellItem, and TShellItemIterator
///
/// The following clases are completely implemented in the header file:
/// TShellMalloc, TExtractIcon, TContextMenu, TDataObject, TDropTarget
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/shellitm.h>
#if defined(__BORLANDC__)
# pragma option -w-ccc // Disable "Condition is always true/false"
#endif
using namespace std;
namespace owl {
OWL_DIAGINFO;
DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlShell, 1, 0); // Shell Diagostic Group
////////////////////////////////////////////////////////////////////
//
// class TShell
// ~~~~~ ~~~~~~
// delay loading SHELL32.DLL/SHELL.DLL
static const tchar shellStr[] = _T("SHELL32.DLL");
static const char DragAcceptFilesStr[] = "DragAcceptFiles";
static const char DragFinishStr[] = "DragFinish";
static const char DragQueryPointStr[] = "DragQueryPoint";
static const char SHAddToRecentDocsStr[]= "SHAddToRecentDocs";
static const char SHChangeNotifyStr[] = "SHChangeNotify";
static const char SHGetMallocStr[] = "SHGetMalloc";
static const char SHGetSpecialFolderLocationStr[] = "SHGetSpecialFolderLocation";
static const char SHGetDesktopFolderStr[] = "SHGetDesktopFolder";
#if defined(UNICODE)
static const char DragQueryFileStr[] = "DragQueryFileW";
static const char ExtractIconStr[] = "ExtractIconW";
static const char ShellExecuteStr[] = "ShellExecuteW";
static const char Shell_NotifyIconStr[] = "Shell_NotifyIconW";
static const char SHBrowseForFolderStr[]= "SHBrowseForFolderW";
static const char SHFileOperationStr[] = "SHFileOperationW";
static const char SHGetFileInfoStr[] = "SHGetFileInfoW";
static const char SHGetPathFromIDListStr[] = "SHGetPathFromIDListW";
#else
static const char DragQueryFileStr[] = "DragQueryFileA";
static const char ExtractIconStr[] = "ExtractIconA";
static const char ShellExecuteStr[] = "ShellExecuteA";
static const char Shell_NotifyIconStr[] = "Shell_NotifyIconA";
static const char SHBrowseForFolderStr[]= "SHBrowseForFolderA";
static const char SHFileOperationStr[] = "SHFileOperationA";
static const char SHGetFileInfoStr[] = "SHGetFileInfoA";
static const char SHGetPathFromIDListStr[] = "SHGetPathFromIDListA";
#endif
//
/// Returns TModule object wrapping the handle of the SHELL[32].DLL module
//
TModule&
TShell::GetModule()
{
static TModule shellModule(shellStr, true, true, false);
return shellModule;
}
//
/// Invokes 'DragAcceptFiles' indirectly
//
void
TShell::DragAcceptFiles(HWND p1, BOOL p2)
{
static TModuleProcV2<HWND,BOOL>
dragAcceptFiles(GetModule(), DragAcceptFilesStr);
dragAcceptFiles(p1,p2);
}
//
/// Invokes 'DragFinish' indirectly
//
void
TShell::DragFinish(HDROP p1)
{
static TModuleProcV1<HDROP>
dragFinish(GetModule(), DragFinishStr);
dragFinish(p1);
}
//
/// Invokes 'DragQueryFile' indirectly
//
UINT
TShell::DragQueryFile(HDROP p1, UINT p2, LPTSTR p3, UINT p4)
{
static TModuleProc4<UINT,HDROP,UINT,LPTSTR,UINT>
dragQueryFile(GetModule(), DragQueryFileStr);
return dragQueryFile(p1, p2, p3, p4);
}
//
/// Invokes 'DragQueryPoint' indirectly
//
BOOL
TShell::DragQueryPoint(HDROP p1, LPPOINT p2)
{
static TModuleProc2<BOOL,HDROP,LPPOINT>
dragQueryPoint(GetModule(), DragQueryPointStr);
return dragQueryPoint(p1, p2);
}
//
/// Invokes 'ExtractIcon' indirectly
//
HICON
TShell::ExtractIcon(HINSTANCE p1, LPCTSTR p2, UINT p3)
{
static TModuleProc3<HICON,HINSTANCE,LPCTSTR,UINT>
extractIcon(GetModule(), ExtractIconStr);
return extractIcon(p1, p2, p3);
}
//
/// Invokes 'SHAddToRecentDocs' indirectly
//
void
TShell::SHAddToRecentDocs(UINT p1, LPCVOID p2)
{
static TModuleProcV2<UINT,LPCVOID>
sHAddToRecentDocs(GetModule(), SHAddToRecentDocsStr);
sHAddToRecentDocs(p1, p2);
}
//
/// Invokes 'SHBrowseForFolder' indirectly
//
LPITEMIDLIST
TShell::SHBrowseForFolder(LPBROWSEINFO p1)
{
static TModuleProc1<LPITEMIDLIST,LPBROWSEINFO>
sHBrowseForFolder(GetModule(), SHBrowseForFolderStr);
return sHBrowseForFolder(p1);
}
//
/// Invokes 'SHChangeNotify' indirectly
//
void
TShell::SHChangeNotify(LONG p1, UINT p2, LPCVOID p3, LPCVOID p4)
{
static TModuleProcV4<LONG,UINT,LPCVOID,LPCVOID>
sHChangeNotify(GetModule(), SHChangeNotifyStr);
sHChangeNotify(p1, p2, p3, p4);
}
//
/// Invokes 'SHFileOperation' indirectly
//
int
TShell::SHFileOperation(LPSHFILEOPSTRUCT p1)
{
static TModuleProc1<int,LPSHFILEOPSTRUCT>
sHFileOperation(GetModule(), SHFileOperationStr);
return sHFileOperation(p1);
}
//
/// Invokes 'SHGetDesktopFolder' indirectly
//
HRESULT
TShell::SHGetDesktopFolder(LPSHELLFOLDER* p1)
{
static TModuleProc1<HRESULT,LPSHELLFOLDER*>
sHGetDesktopFolder(GetModule(), SHGetDesktopFolderStr);
return sHGetDesktopFolder(p1);
}
//
/// Invokes 'SHGetFileInfo' indirectly
//
DWORD
TShell::SHGetFileInfo(LPCTSTR p1, DWORD p2, SHFILEINFO * p3,
UINT p4, UINT p5)
{
static TModuleProc5<UINT,LPCTSTR,DWORD,SHFILEINFO *,UINT,UINT>
sHGetFileInfo(GetModule(), SHGetFileInfoStr);
return sHGetFileInfo(p1, p2, p3, p4, p5);
}
//
/// Invokes 'SHGetMalloc' indirectly
//
HRESULT
TShell::SHGetMalloc(LPMALLOC* p1)
{
static TModuleProc1<HRESULT,LPMALLOC*>
sHGetMalloc(GetModule(), SHGetMallocStr);
return sHGetMalloc(p1);
}
//
/// Invokes 'SHGetPathFromIDList' indirectly
//
BOOL
TShell::SHGetPathFromIDList(LPCITEMIDLIST p1, LPTSTR p2)
{
static TModuleProc2<BOOL,LPCITEMIDLIST,LPTSTR>
sHGetPathFromIDList(GetModule(), SHGetPathFromIDListStr);
return sHGetPathFromIDList(p1, p2);
}
//
/// Invokes 'SHGetSpecialFolderLocation' indirectly
//
HRESULT
TShell::SHGetSpecialFolderLocation(HWND p1, int p2, LPITEMIDLIST* p3)
{
static TModuleProc3<HRESULT,HWND,int,LPITEMIDLIST*>
sHGetSpecialFolderLocation(GetModule(), SHGetSpecialFolderLocationStr);
return sHGetSpecialFolderLocation(p1, p2, p3);
}
//
/// Invokes 'ShellExecute' indirectly
//
HINSTANCE
TShell::ShellExecute(HWND p1, LPCTSTR p2, LPCTSTR p3, LPCTSTR p4,
LPCTSTR p5, INT p6)
{
static TModuleProc6<HINSTANCE,HWND,LPCTSTR,LPCTSTR,LPCTSTR,LPCTSTR,INT>
shellExecute(GetModule(), ShellExecuteStr);
return shellExecute(p1, p2, p3, p4, p5, p6);
}
//
/// Invokes 'Shell_NotifyIcon' indirectly
//
BOOL
TShell::Shell_NotifyIcon(DWORD p1, PNOTIFYICONDATA p2)
{
static TModuleProc2<BOOL,DWORD,PNOTIFYICONDATA>
shell_NotifyIcon(GetModule(), Shell_NotifyIconStr);
return shell_NotifyIcon(p1, p2);
}
//////////////////////////////////////////////////////////////////////////
//
/// protected function used internally to free PIDL memory with the shell's allocator
//
void
TPidl::FreePidl()
{
if (Pidl) {
TShellMalloc malloc;
malloc->Free(Pidl);
Pidl = 0;
}
}
//
/// GetItemCount returns the number of identifiers in the
/// identifier list (pidl)
//
long
TPidl::GetItemCount() const
{
long cnt = 0;
if (Pidl) {
LPITEMIDLIST pidl = Pidl;
while (pidl->mkid.cb) {
cnt++;
pidl = Next(pidl);
}
}
return cnt;
}
//
/// GetSize returns the size (in bytes) of the PIDL
//
ulong
TPidl::GetSize() const
{
long size = sizeof(Pidl->mkid.cb);
LPITEMIDLIST pidl = Pidl;
while (pidl->mkid.cb) {
size += pidl->mkid.cb;
pidl = Next(pidl);
}
return size;
}
//
/// CopyPidl copies a pidl (allocates memory with the shell's allocator)
//
LPITEMIDLIST
TPidl::CopyPidl() const
{
if (!Pidl)
return 0;
else {
LPITEMIDLIST newPidl;
TShellMalloc malloc;
// Expanded by Val Ovechkin 11:57 AM 6/3/98
void *p = malloc->Alloc(GetSize());
newPidl = reinterpret_cast<LPITEMIDLIST>(p);
WARNX(OwlShell, !newPidl, 0, \
_T("IShellMalloc::Alloc failed. IShellMalloc* = ")\
<< hex << STATIC_CAST(IMalloc*, malloc));
if (!newPidl)
TXShell::Raise(IDS_SHELLALLOCFAIL);
LPITEMIDLIST pidlSrc = Pidl;
LPITEMIDLIST pidlDest = newPidl;
while (pidlSrc->mkid.cb) {
CopyMemory(pidlDest, pidlSrc, pidlSrc->mkid.cb);
pidlDest = Next(pidlDest);
pidlSrc = Next(pidlSrc);
}
ushort zero = 0;
CopyMemory(pidlDest, &zero, sizeof(ushort));
return newPidl;
}
}
//
/// GetLastItem returns the last item in an identifier list
/// for file system items, this is the rightmost part of a path
/// e.g., GetLastItem() on a pidl representing
/// \code
/// "c:\\dir1\\dir2\\dir3\\file1"
/// \endcode
/// returns "file1"
TPidl
TPidl::GetLastItem() const
{
if (!Pidl)
return TPidl();
else {
long cnt = GetItemCount();
LPITEMIDLIST pidl = Pidl;
for (int i = 0; i < cnt - 1; i++)
pidl = Next(pidl);
LPITEMIDLIST newPidl;
TShellMalloc malloc;
// Expanded by Val Ovechkin 11:57 AM 6/3/98
void *p = malloc->Alloc(pidl->mkid.cb + sizeof (pidl->mkid.cb));
newPidl = reinterpret_cast<LPITEMIDLIST>(p);
WARNX(OwlShell, !newPidl, 0, _T("IShellMalloc::Alloc failed. IShellMalloc* = ")
<< hex << STATIC_CAST(IMalloc*, malloc));
if (!newPidl)
TXShell::Raise(IDS_SHELLALLOCFAIL);
CopyMemory(newPidl, pidl, pidl->mkid.cb);
LPITEMIDLIST pidlDest = Next(newPidl);
USHORT zero = 0;
CopyMemory(pidlDest, &zero, sizeof (USHORT));
return newPidl;
}
}
//
/// StripLastItem returns a pidl stipped of its last (rightmost) item
//
TPidl
TPidl::StripLastItem() const
{
int i;
if (!Pidl)
return TPidl();
else {
// calculate the size less the last item id
long cnt = GetItemCount();
if (cnt < 2)
return TPidl();
long size = sizeof(Pidl->mkid.cb);
LPITEMIDLIST pidl = Pidl;
for (i = 0; i < cnt - 1; i++) {
size += pidl->mkid.cb;
pidl = Next(pidl);
}
LPITEMIDLIST newPidl;
TShellMalloc malloc;
// Expanded by Val Ovechkin 11:57 AM 6/3/98
void *p = malloc->Alloc(size);
newPidl = reinterpret_cast<LPITEMIDLIST>(p);
WARNX(OwlShell, !newPidl, 0, _T("IShellMalloc::Alloc failed. IShellMalloc* = ")
<< hex << STATIC_CAST(IMalloc*, malloc));
if (!newPidl)
TXShell::Raise(IDS_SHELLALLOCFAIL);
LPITEMIDLIST pidlSrc = Pidl;
LPITEMIDLIST pidlDest = newPidl;
for (i = 0; i < cnt - 1; i++) {
CopyMemory(pidlDest, pidlSrc, pidlSrc->mkid.cb);
pidlDest = Next(pidlDest);
pidlSrc = Next(pidlSrc);
}
USHORT zero = 0;
CopyMemory(pidlDest, &zero, sizeof (USHORT));
return newPidl;
}
}
/// TShellItem constructor to make a TShellItem for a file or directory in the file system.
/// If the throwOnInvalidPath argument is true, an exception will be raised if the path is
/// not valid or if the file or directory does not exist. If this argument is false, no
/// exception will be raised and the Valid() function should be called to make sure it
/// returns true. If Valid() returns false, the TShellItem does not represent a object
/// in the shell namespace.
//
TShellItem::TShellItem(const tchar* path, bool throwOnInvalidPath, HWND windowOwner)
:TComRef<IShellFolder>(0), Pidl(0), ParentFolder(0)
{
TShellItem desktop(TShellItem::Desktop, windowOwner);
TCreateStruct cs;
ulong eaten;
HRESULT hr = desktop.ParseDisplayName(cs, path, &eaten, windowOwner);
WARNX(OwlShell, FAILED(hr), 0, _T("Invalid path. Path = ") << path);
if (FAILED(hr))
{
if (throwOnInvalidPath)
TXShell::Raise(IDS_INVALIDPATH);
else
return;
}
Pidl = cs.Pidl.GetLastItem();
// Expanded by Val Ovechkin 11:57 AM 6/3/98
void *p = STATIC_CAST(IShellFolder**, ParentFolder);
hr = desktop->BindToObject(cs.Pidl.StripLastItem(), 0, IID_IShellFolder,
REINTERPRET_CAST(void **, p));
// hr = desktop->BindToObject(cs.Pidl.StripLastItem(), 0, IID_IShellFolder,
// REINTERPRET_CAST(void **,
// STATIC_CAST(IShellFolder**, ParentFolder)));
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::BindToObject failed. TShellItem* = ")
<< hex << (LPVOID)this << _T(" IShellFolder* = ")
<< STATIC_CAST(IShellFolder*, ParentFolder));
TXShell::Check(hr, IDS_BINDTOOBJECTFAIL);
}
//
/// TShellItem constructor to make a TShellItem for a special folder. Special
/// folders can be any specified in the TSpecialFolderKind enum.
///
/// (see MS doc for SHGetSpecialFolderLocation for more information,
/// Programmer's Guide to MS Windows 95, MS Press, p. 209)
//
TShellItem::TShellItem(const TSpecialFolderKind kind, HWND windowOwner)
:TComRef<IShellFolder>(0), Pidl(0), ParentFolder(0)
{
IShellFolder* desktop;
HRESULT hr = TShell::SHGetDesktopFolder(&desktop);
WARNX(OwlShell, FAILED(hr), 0, _T("::SHGetDesktopFolder failed."));
TXShell::Check(hr, IDS_SHGETDESKTOPFAIL);
TPidl pidl;
hr = TShell::SHGetSpecialFolderLocation(windowOwner, kind, pidl);
WARNX(OwlShell, FAILED(hr), 0, \
_T("::SHGetSpecialFolderLocation failed. kind = ") << kind);
TXShell::Check(hr, IDS_SHGETSPECIALFAIL);
if (kind == Desktop) {
I = desktop; // Gets itself for the ParentFolder below
I->AddRef();
}
else {
hr = desktop->BindToObject(pidl, 0, IID_IShellFolder,
REINTERPRET_CAST(void **, STATIC_CAST(IShellFolder**, *this)));
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::BindToObject failed. Desktop IShellFolder* = ")
<< hex << STATIC_CAST(IShellFolder*, desktop));
TXShell::Check(hr, IDS_BINDTOOBJECTFAIL);
}
ParentFolder = desktop; // It's not clearly documented but we're assuming
Pidl = pidl; // that all special folders are children of the
} // desktop/root.
//
/// TShellItem constructor to make a TShellItem from a TCreateStruct
///
/// TCreateStruct contains a TPidl and a
/// TComRef<IShellFolder>ParentFolder which represents the parent folder
///
/// TCreateStructs are returned by TShellItem::GetParentFolder() and by the
/// following TShellItemIterator functions:
/// operator ++(pre- &post-fix), operator -- (pre- & post-fix), operator [](),
/// and Current()
///
/// TCreateStructs are also returned as out parameters of the following TShellItem
/// functions:
/// BrowseForFolder(), ParseDisplayName()
//
TShellItem::TShellItem(const TCreateStruct& cs)
:TComRef<IShellFolder>(0), Pidl(cs.Pidl),
ParentFolder(cs.ParentFolder)
{
}
//
/// TShellIterm constructor to make a TShellItem from a ITEMIDLIST(pidl) and a
/// TComRef<IShellFolder> (parent folder)
//
//
TShellItem::TShellItem(LPITEMIDLIST pidl, const TComRef<IShellFolder>& parentFolder)
:TComRef<IShellFolder>(0), Pidl(pidl), ParentFolder(parentFolder)
{
}
//
/// TShellItem constructor to make a TShellItem from a TPidl and a
/// TComRef<IShellFolder> (parent folder)
//
TShellItem::TShellItem(const TPidl& pidl, const TComRef<IShellFolder>& parentFolder)
:TComRef<IShellFolder>(0), Pidl(pidl), ParentFolder(parentFolder)
{
}
//
/// TShellItem copy constructor
//
TShellItem::TShellItem(const TShellItem& source)
:TComRef<IShellFolder>(source), Pidl(source.Pidl),
ParentFolder(source.ParentFolder)
{
}
//
/// TShellItem assignment (from another TShellItem)
//
TShellItem& TShellItem::operator = (const TShellItem &source)
{
if (&source != this) {
TComRef<IShellFolder>::operator=(source);
Pidl = source.Pidl;
ParentFolder = source.ParentFolder;
}
return *this;
}
//
/// TShellItem assignment (from a TCreateStruct)
//
TShellItem& TShellItem::operator = (const TCreateStruct& cs)
{
Clear();
Pidl = cs.Pidl;
ParentFolder = cs.ParentFolder;
return *this;
}
//
/// GetAttributes gets Capabilities, Display, Contents, & Misc. Attributes
/// with a single call
///
/// 'validateCachedInfo' defaults to false. If true, the shell will not
/// rely on cached information.
///
/// See MS doc for IShellFolder::GetAttributesOf for more information, Programmer's
/// Guide to MS Windows 95, MS Press, pp. 194-195.
//
ulong
TShellItem::GetAttributes(const ulong reqAttrib, const bool validateCachedInfo) const
{
EnforceValidity();
ulong req = reqAttrib | ((validateCachedInfo)? SFGAO_VALIDATE: 0);
HRESULT hr = ParentFolder->GetAttributesOf(1, Pidl, &req);
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::GetAttributesOf failed. TShellItem* = ")
<< hex << (LPVOID)this << _T(" Parent IShellFolder* = ")
<< STATIC_CAST(IShellFolder*, *CONST_CAST(TComRef<IShellFolder>*, &ParentFolder)));
TXShell::Check(hr, IDS_GETATTRIBUTESOFFAIL);
return req;
}
//
/// GetAttribute is a protected function used by all the attribute functions except
/// IsDesktop.
///
/// \note GetAttribute gets attributes one at a time. GetAttributes (with the 's')
/// is used to get any number of attributes with one call.
//
bool
TShellItem::GetAttribute(const TAttribute at, const bool validateCachedInfo) const
{
EnforceValidity();
ulong req = at;
req = GetAttributes(req, validateCachedInfo);
return (req & at);
}
// Attributes - Additional (Not part of GetAttributes)
//
/// IsDesktop returns true if the TShellItem respresents the shell's desktop.
//
bool
TShellItem::IsDesktop() const
{
EnforceValidity();
TShellItem desktop(Desktop);
return (I == desktop.I);
}
//
/// GetParentFolder returns a TCreateStruct representing the folder which contains
/// the TShellItem.
//
TShellItem::TCreateStruct
TShellItem::GetParentFolder() const
{
EnforceValidity();
TCreateStruct cs;
if (IsDesktop()) {
cs.Pidl = Pidl; // return self
cs.ParentFolder = ParentFolder; // ParentFolder is itself too
}
else {
TShellItem desktop(Desktop);
TPidl newFullPidl = GetFullyQualifiedPidl().StripLastItem();
cs.Pidl = newFullPidl.GetLastItem();
// Expanded by Val Ovechkin 11:57 AM 6/3/98
void *p = STATIC_CAST(IShellFolder**, cs.ParentFolder);
HRESULT hr = desktop->BindToObject(newFullPidl.StripLastItem(), 0, IID_IShellFolder,
REINTERPRET_CAST(void **, p));
// HRESULT hr = desktop->BindToObject(newFullPidl.StripLastItem(), 0, IID_IShellFolder,
// REINTERPRET_CAST(void **, STATIC_CAST(IShellFolder**, cs.ParentFolder)));
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::BindToObject failed. TShellItem* = ")
<< hex << (LPVOID)this << _T(" Parent LPITEMIDLIST = ")
<< static_cast<LPITEMIDLIST>(newFullPidl.StripLastItem()));
TXShell::Check(hr, IDS_BINDTOOBJECTFAIL);
}
return cs;
}
//
/// GetFullyQualifiedPidl returns a TPidl that is fully qualified (from the desktop).
//
TPidl
TShellItem::GetFullyQualifiedPidl() const
{
EnforceValidity();
TString name = GetDisplayName(ForParsing);
TShellItem desktop(Desktop);
TCreateStruct cs;
desktop.ParseDisplayName(cs, name);
return cs.Pidl;
}
//
/// GetExeType returns the type of executable that is contained in the TShellItem
/// Also, the optional major and minor args return version numbers
/// e.g., return = WindowsPE, major = 4, minor = 0
///
/// See MS doc for SHGetFileInfo for more information, Programmer's Guide to MS
/// Windows 95, MS Press, pp. 205-207.
//
TShellItem::TExeKind
TShellItem::GetExeType(uint* major, uint* minor) const
{
EnforceValidity();
if (!IsPartOfFileSystem() || IsFolder() || ContainsSubFolder())
return NonExecutable;
DWORD rc = TShell::SHGetFileInfo(GetPath(), 0, 0, 0, SHGFI_EXETYPE);
short exeType = LOWORD(rc);
// Expanded by Val Ovechkin 11:57 AM 6/3/98
void *p = &exeType;
const char* charType = REINTERPRET_CAST(const char*, p);
// const char* charType = REINTERPRET_CAST(const char*, &exeType);
if (major)
*major = HIBYTE(HIWORD(rc));
if (minor)
*minor = LOBYTE(HIWORD(rc));
WARNX(OwlShell,
rc && ::strncmp("PE", charType, 2) && ::strncmp("NE", charType, 2) && ::strncmp("MZ", charType, 2),
0, "::GetFileInfo returned an invalid exeType. exeType = " << exeType);
if (!rc)
return NonExecutable;
else if (!::strncmp("PE", charType, 2))
return (HIWORD(rc))? WindowsPE: Win32Console;
else if (!::strncmp("NE", charType, 2))
return WindowsNE;
else if (!::strncmp("MZ", charType, 2))
return MSDOS;
else
TXShell::Raise(IDS_GETFILEINFOFAIL);
return NonExecutable; // To satisfy compiler
}
//
/// GetTypeName returns the type of item that is represented by this TShellItem
/// examples: application, notepad file, ...
///
/// See MS doc for SHGetFileInfo for more information, Programmer's Guide to MS
/// Windows 95, MS Press, pp. 205-207.
//
TString
TShellItem::GetTypeName() const
{
EnforceValidity();
TString typeName;
SHFILEINFO fi;
LPCITEMIDLIST pidl = GetPidl();
DWORD_PTR rc = ::SHGetFileInfo(
reinterpret_cast<const tchar*>(pidl),
0, &fi, sizeof (fi), SHGFI_TYPENAME | SHGFI_PIDL);
WARNX(OwlShell, !rc, 0, _T("::GetFileInfo failed. TShellItem = ") << (LPCSTR)GetDisplayName());
if (!rc)
TXShell::Raise(IDS_GETFILEINFOFAIL);
typeName = fi.szTypeName;
return typeName;
}
//
/// GetDisplayName returns a name for this TShellItem that is suitable to display
/// to the user.
///
/// The three options are:
///
/// - \c \b Normal - Suitable for displaying by itself
/// - \c \b InFolder - Suitable for displaying within its parent folder
/// - \c \b ForParsing - Suitable to pass to the ParseDiplayName member function
///
/// See MS doc for IShellFolder::GetDisplayNameOf for more information,
/// Programmer's Guide to MS Windows 95, MS Press, pp. 197-198.
//
TString
TShellItem::GetDisplayName(const TDisplayNameKind kind) const
{
EnforceValidity();
TString s;
STRRET strRet;
strRet.uType = STRRET_WSTR;
HRESULT hr = ParentFolder->GetDisplayNameOf(Pidl, kind, &strRet);
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::GetDisplayNameOf failed. TShellItem* = ")
<< hex << (LPVOID)this);
TXShell::Check(hr, IDS_GETDISPLAYNAMEFAIL);
switch (strRet.uType) {
case STRRET_WSTR:{ // Case requested
s = strRet.pOleStr;
TShellMalloc malloc;
malloc->Free(strRet.pOleStr);
}
break;
case STRRET_CSTR: // C-string
s = strRet.cStr;
break;
case STRRET_OFFSET: // Offset into the piddle
s = reinterpret_cast<const char*>(static_cast<LPCITEMIDLIST>(Pidl)) + strRet.uOffset;
break;
}
return s;
}
//
/// GetPath returns the fully qualified path representing this TShellItem
//
TString
TShellItem::GetPath() const
{
tchar path[_MAX_PATH];
bool success = TShell::SHGetPathFromIDList(GetFullyQualifiedPidl(), path);
WARNX(OwlShell, !success, 0, "::SHGetPathFromIDList failed. TShellItem* = "
<< hex << (LPVOID)this);
if (!success)
TXShell::Raise(IDS_GETPATHFROMPIDLFAIL);
TString s;
s = path;
return s;
}
//
/// Rename renames this TShellItem to newName.
///
/// kind indicates the kind of name being passed (Normal, InFolder, or ForParsing)
/// (see GetDisplayName for description of these)
///
/// See MS doc for IShellFolder::SetNameOf for more information, Programmer's
/// Guide to MS Windows 95, MS Press, pp. 198-199.
//
void
TShellItem::Rename(const tchar *newName, HWND windowOwner, const TDisplayNameKind kind)
{
LPITEMIDLIST pidl;
if (!CanBeRenamed())
TXShell::Raise(IDS_SHELLRENAMEFAIL);
// This stuff needed for ::SHChangeNotify
bool isFolder = IsFolder();
tchar oldPath[_MAX_PATH];
memset(oldPath, 0, sizeof(oldPath));
::_tcscpy(oldPath, GetDisplayName(ForParsing));
TShellItem parentFolder = GetParentFolder();
tchar newPath[_MAX_PATH];
memset(newPath, 0, sizeof (newPath));
::_tcscpy(newPath, parentFolder.GetDisplayName(ForParsing));
_tcscat(newPath, newName);
HRESULT hr = ParentFolder->SetNameOf(windowOwner, Pidl, TString(newName), kind, &pidl);
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::SetNameOf failed. TShellItem* = ")
<< hex << (LPVOID)this << _T(" Parent IShellFolder* = ")
<< STATIC_CAST(IShellFolder*, ParentFolder));
TXShell::Check(hr, IDS_SETNAMEOFFAIL);
Pidl = pidl;
TShell::SHChangeNotify(isFolder? SHCNE_RENAMEFOLDER: SHCNE_RENAMEITEM,
SHCNF_PATH /*| SHCNF_FLUSH*/, STATIC_CAST(const tchar*, oldPath),
newPath);
}
//
/// Copy copies this TShellItem to destination path
///
/// destIsFolder must tell whether or not dest is a folder
///
/// flags are any combination of TFileOpFlags
/// (see MS doc for SHFILEOPSTRUCT for meanings of these flags,
/// Programmer's Guide to MS Windows 95, MS Press, p. 215)
///
/// Title is the title displayed in the progress dialog box
///
/// windowOwner should be set to the owner of the progress dialog box
///
/// See MS doc for SHFileOperation for more information, Programmer's Guide to
/// MS Windows 95, MS Press, p. 204.
//
bool
TShellItem::Copy(const tchar* dest, const bool destIsFolder,
const USHORT flags, const tchar* title,
HWND windowOwner) const
{
EnforceValidity();
SHFILEOPSTRUCT fo;
if (!CanBeCopied())
TXShell::Raise(IDS_SHELLCOPYFAIL);
fo.hwnd = windowOwner;
fo.wFunc = FO_COPY;
tchar srcPath[_MAX_PATH];
::memset(srcPath, 0, sizeof (srcPath));
::_tcscpy(srcPath, GetDisplayName(ForParsing));
fo.pFrom = srcPath;
tchar destPath[_MAX_PATH];
::memset(destPath, 0, sizeof (destPath));
::_tcscpy(destPath, dest);
fo.pTo = destPath;
fo.fFlags = flags;
fo.fAnyOperationsAborted = FALSE;
fo.hNameMappings = 0;
fo.lpszProgressTitle = title;
if (!destIsFolder)
fo.fFlags |= FOF_MULTIDESTFILES; // Destination is a filename, not a folder
bool isFolder = IsFolder();
int rc = TShell::SHFileOperation(&fo);
if (rc || fo.fAnyOperationsAborted)
return false;
TShell::SHChangeNotify(isFolder? SHCNE_MKDIR: SHCNE_CREATE, SHCNF_PATH /*| SHCNF_FLUSH*/,
STATIC_CAST(const tchar*, GetDisplayName(ForParsing)), 0);
return true;
}
//
/// Copy copies this TShellItem to destination TShellItem
///
/// flags are any combination of TFileOpFlags
/// (see MS doc for SHFILEOPSTRUCT for meanings of these flags,
/// Programmer's Guide to MS Windows 95, MS Press, p. 215)
///
/// Title is the title displayed in the progress dialog box
///
/// windowOwner should be set to the owner of the progress dialog box
///
/// See MS doc for SHFileOperation for more information, Programmer's Guide to
/// MS Windows 95, MS Press, p. 204.
//
bool
TShellItem::Copy(const TShellItem& dest, const USHORT flags, const tchar* title,
HWND windowOwner) const
{
return Copy(dest.GetDisplayName(ForParsing), dest.IsFolder(), flags, title, windowOwner);
}
//
/// Move moves this TShellItem to destination path (which must be a folder)
///
/// flags are any combination of TFileOpFlags
/// (see MS doc for SHFILEOPSTRUCT for meanings of these flags,
/// Programmer's Guide to MS Windows 95, MS Press, p. 215)
///
/// Title is the title displayed in the progress dialog box
///
/// windowOwner should be set to the owner of the progress dialog box
///
/// See MS doc for SHFileOperation for more information, Programmer's Guide to
/// MS Windows 95, MS Press, p. 204.
//
bool
TShellItem::Move(const tchar* destFolder, const USHORT flags, const tchar* title,
HWND windowOwner)
{
EnforceValidity();
SHFILEOPSTRUCT fo;
if (!CanBeMoved())
TXShell::Raise(IDS_SHELLMOVEFAIL);
fo.hwnd = windowOwner;
fo.wFunc = FO_MOVE;
tchar oldPath[_MAX_PATH];
::memset(oldPath, 0, sizeof (oldPath));
::_tcscpy(oldPath, GetDisplayName(ForParsing));
fo.pFrom = oldPath;
tchar destPath[_MAX_PATH];
::memset(destPath, 0, sizeof (destPath));
::_tcscpy(destPath, destFolder);
fo.pTo = destPath;
fo.fFlags = flags;
fo.fAnyOperationsAborted = FALSE;
fo.hNameMappings = 0;
fo.lpszProgressTitle = title;
bool isFolder = IsFolder();
int rc = TShell::SHFileOperation(&fo);
if (rc || fo.fAnyOperationsAborted)
return false;
tchar newPath[_MAX_PATH];
::_tcscpy(newPath, destFolder);
#if defined(BI_DBCS_SUPPORT)
if (*AnsiPrev(newPath, newPath + ::strlen(newPath)) != '\\')
#else
if (newPath[::_tcslen(newPath)-1] != _T('\\'))
#endif
_tcscat(newPath, _T("\\"));
_tcscat(newPath, GetDisplayName());
*this = TShellItem(newPath); // change myself to the new location
// Notify deletion of old path
TShell::SHChangeNotify(isFolder? SHCNE_RMDIR: SHCNE_DELETE, SHCNF_PATH /*| SHCNF_FLUSH*/,
STATIC_CAST(const tchar*, oldPath), 0);
// Notify creation of new path
TShell::SHChangeNotify(isFolder? SHCNE_MKDIR: SHCNE_CREATE, SHCNF_PATH /*| SHCNF_FLUSH*/,
STATIC_CAST(const tchar*, GetDisplayName(ForParsing)), 0);
return true;
}
//
/// Move moves this TShellItem to destination TShellItem (which must be a folder)
///
/// flags are any combination of TFileOpFlags
/// (see MS doc for SHFILEOPSTRUCT for meanings of these flags,
/// Programmer's Guide to MS Windows 95, MS Press, p. 215)
///
/// Title is the title displayed in the progress dialog box
///
/// windowOwner should be set to the owner of the progress dialog box
///
/// See MS doc for SHFileOperation for more information, Programmer's Guide to
/// MS Windows 95, MS Press, p. 204.
//
bool
TShellItem::Move(const TShellItem& destFolder, const USHORT flags,
const tchar* title, HWND windowOwner)
{
return Move(destFolder.GetDisplayName(ForParsing), flags, title, windowOwner);
}
//
/// Delete deletes this TShellItem
///
/// flags are any combination of TFileOpFlags
/// (see MS doc for SHFILEOPSTRUCT for meanings of these flags,
/// Programmer's Guide to MS Windows 95, MS Press, p. 215)
///
/// Title is the title displayed in the progress dialog box
///
/// windowOwner should be set to the owner of the progress dialog box
///
/// See MS doc for SHFileOperation for more information, Programmer's Guide to
/// MS Windows 95, MS Press, p. 204.
//
bool
TShellItem::Delete(const USHORT flags, const tchar* title, HWND windowOwner)
{
EnforceValidity();
if (!Pidl) // Nothing to delete!
return true;
if (!CanBeDeleted())
TXShell::Raise(IDS_SHELLDELETEFAIL);
SHFILEOPSTRUCT fo;
fo.hwnd = windowOwner;
fo.wFunc = FO_DELETE;
tchar path[_MAX_PATH];
memset(path, 0, sizeof (path));
::_tcscpy(path, GetDisplayName(ForParsing)); // Also used to reconstruct if delete fails/user aborts
fo.pFrom = path;
fo.pTo = 0;
fo.fFlags = flags;
fo.fAnyOperationsAborted = FALSE;
fo.hNameMappings = 0;
fo.lpszProgressTitle = title;
bool isFolder = IsFolder(); // Needed for ::SHChangeNotify
// About to delete: save and release everything
//
TComRef<IShellFolder>::operator=(0);
Pidl = 0;
ParentFolder = 0;
int rc = TShell::SHFileOperation(&fo);
if (rc || fo.fAnyOperationsAborted) {
*this = TShellItem(path); // restore everything
return false;
}
TShell::SHChangeNotify(isFolder? SHCNE_RMDIR: SHCNE_DELETE, SHCNF_PATH /*| SHCNF_FLUSH*/,
path, 0);
return true;
}
//
/// GetExtractIcon returns an interface pointer to this TShellItem's IExtractIcon
/// OLE interface
///
/// (see MS doc for information on IExtractIcon,
/// Programmer's Guide to MS Windows 95, MS Press, pp. 242-244)
//
TExtractIcon
TShellItem::GetExtractIcon(HWND windowOwner)
{
EnforceValidity();
IExtractIcon* iface;
// Expanded by Val Ovechkin 12:29 PM 6/3/98
void *p = &iface;
HRESULT hr = ParentFolder->GetUIObjectOf(windowOwner, 1, Pidl, IID_IExtractIcon,
0, REINTERPRET_CAST(void**, p));
// HRESULT hr = ParentFolder->GetUIObjectOf(windowOwner, 1, Pidl, IID_IExtractIcon,
// 0, REINTERPRET_CAST(void**, &iface));
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::GetUIObjectOf(IExtractIcon) ")
<< _T("failed. TShellItem* = ")
<< hex << (LPVOID)this << _T(" Parent IShellFolder* = ")
<< STATIC_CAST(IShellFolder*, ParentFolder));
TXShell::Check(hr, IDS_GETUIOBJECTOFFAIL);
return TExtractIcon(iface);
}
//
/// GetContextMenu returns an interface pointer to this TShellItem's IContextMenu
/// OLE interface
///
/// (see MS doc for information on IContextMenu,
/// Programmer's Guide to MS Windows 95, MS Press, pp. 237-240)
//
TContextMenu
TShellItem::GetContextMenu(HWND windowOwner)
{
EnforceValidity();
IContextMenu* iface;
// Expanded by Val Ovechkin 12:29 PM 6/3/98
void *p = &iface;
HRESULT hr = ParentFolder->GetUIObjectOf(windowOwner, 1, Pidl, IID_IContextMenu,
0, REINTERPRET_CAST(void**, p));
// HRESULT hr = ParentFolder->GetUIObjectOf(windowOwner, 1, Pidl, IID_IContextMenu,
// 0, REINTERPRET_CAST(void**, &iface));
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::GetUIObjectOf(IContextMenu) ")
<< _T("failed. TShellItem* = ")
<< hex << (LPVOID)this << _T(" Parent IShellFolder* = ")
<< STATIC_CAST(IShellFolder*, ParentFolder));
TXShell::Check(hr, IDS_GETUIOBJECTOFFAIL);
return TContextMenu(iface);
}
//
/// GetDataObject returns an interface pointer to this TShellItem's IDataObject
/// OLE interface
///
/// (see MS doc for a very brief description of IDataObject,
/// Programmer's Guide to MS Windows 95, MS Press, p. 113)
//
TDataObject
TShellItem::GetDataObject(HWND windowOwner)
{
EnforceValidity();
IDataObject* iface;
// Expanded by Val Ovechkin 12:29 PM 6/3/98
void *p = &iface;
HRESULT hr = ParentFolder->GetUIObjectOf(windowOwner, 1, Pidl, IID_IDataObject
,
0, REINTERPRET_CAST(void**, p));
// HRESULT hr = ParentFolder->GetUIObjectOf(windowOwner, 1, Pidl, IID_IDataObject,
// 0, REINTERPRET_CAST(void**, &iface));
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::GetUIObjectOf(IDataObject) ")
<< _T("failed. TShellItem* = ")
<< hex << (LPVOID)this << _T(" Parent IShellFolder* = ")
<< STATIC_CAST(IShellFolder*, ParentFolder));
TXShell::Check(hr, IDS_GETUIOBJECTOFFAIL);
return TDataObject(iface);
}
//
/// GetDropTarget returns an interface pointer to this TShellItem's IDropTarget
/// OLE interface
//
TDropTarget
TShellItem::GetDropTarget(HWND windowOwner)
{
EnforceValidity();
IDropTarget* iface;
void *p = &iface;
HRESULT hr = ParentFolder->GetUIObjectOf(windowOwner, 1, Pidl, IID_IDropTarget
,
0, REINTERPRET_CAST(void**, p));
// HRESULT hr = ParentFolder->GetUIObjectOf(windowOwner, 1, Pidl, IID_IDropTarget,
// 0, REINTERPRET_CAST(void**, &iface));
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::GetUIObjectOf(IDropTarget) ")
<< _T("failed. TShellItem* = ")
<< hex << (LPVOID)this << _T(" Parent IShellFolder* = ")
<< STATIC_CAST(IShellFolder*, ParentFolder));
TXShell::Check(hr, IDS_GETUIOBJECTOFFAIL);
return TDropTarget(iface);
}
//
/// GetIcon returns a handle to an icon representing this TShellItem
///
/// size can be Large, Small, or Shell
/// kind can be Link, Open, or Selected
/// (see MS doc on SHGetFileInfo for meanings,
/// Programmer's Guide to MS Windows 95, MS Press, pp. 206-207)
//
HICON
TShellItem::GetIcon(TIconSize size, uint kind)
{
EnforceValidity();
if (!IsPartOfFileSystem()) {
uint inFlags = (size & Shell)? GIL_FORSHELL: 0 | (kind & Open)? GIL_OPENICON: 0;
tchar fileName[_MAX_PATH];
int index;
uint outFlags;
HICON largeIcon;
HICON smallIcon;
TExtractIcon extractIcon = GetExtractIcon();
HRESULT hr = extractIcon->GetIconLocation(inFlags, fileName, COUNTOF(fileName),
&index, &outFlags);
TXShell::Check(hr, IDS_GETICONLOCFAIL);
if (hr != S_FALSE)
if (outFlags & GIL_NOTFILENAME || outFlags & GIL_DONTCACHE) {
hr = extractIcon->Extract(fileName, index, &largeIcon, &smallIcon, GetSystemMetrics(SM_CXICON));
TXShell::Check(hr, IDS_EXTRICONEXTRACTFAIL);
if (hr != S_FALSE)
return (size == Small)? smallIcon: largeIcon;
}
}
// TShellItem is part of file system or couldn't get the icon with
// IExtractIcon::Extract
//
SHFILEINFO fi;
uint flags = SHGFI_ICON | kind | size;
TShell::SHGetFileInfo(reinterpret_cast<const tchar*>(static_cast<LPCITEMIDLIST>(*this)),
0, &fi, sizeof (fi), flags | SHGFI_PIDL);
WARNX(OwlShell, !fi.hIcon, 0, _T("::SHGetFileInfo failed. TShellItem* = ")
<< hex << (LPVOID)this);
if (!fi.hIcon)
TXShell::Raise(IDS_GETFILEINFOFAIL);
return fi.hIcon;
}
//
/// ResolveShortcut resolves this TShellItem (if it's a shortcut) and returns the
/// underlying TShellItem. If this TShellItem is not a shortcut, it just returns itself.
///
/// \note IMPORTANT: This function calls CoCreateInstance. Therefore an application that
/// uses this function MUST have previously called CoInitialize (or OLEInitialize).
///
/// \note IMPORTANT: If the shortcut cannot be resolved, this function will return an
/// empty (invalid) item. One MUST check for this by calling IsValid()
/// upon return from this function.
//
TShellItem
TShellItem::ResolveShortcut(HWND windowOwner)
{
EnforceValidity();
if (!IsShortcut())
return *this;
HRESULT hr;
TComRef<IShellLink> shellLink;
hr = TOle::CoCreateInstance(CLSID_ShellLink, 0, CLSCTX_INPROC_SERVER, IID_IShellLink,
REINTERPRET_CAST(void **, STATIC_CAST(IShellLink**, shellLink)));
TXShell::Check(hr, IDS_CREATESHELLLINKFAIL);
TComRef<IPersistFile> persistFile;
hr = shellLink->QueryInterface(IID_IPersistFile,
REINTERPRET_CAST(void **, STATIC_CAST(IPersistFile**, persistFile)));
TXShell::Check(hr, IDS_LINKQUERYINTRFCFAIL);
TString shortcutFile = GetPath();
hr = persistFile->Load(shortcutFile, STGM_READ);
TXShell::Check(hr, IDS_PERSISTFILELOADFAIL);
hr = shellLink->Resolve(windowOwner, SLR_ANY_MATCH | SLR_UPDATE);
if (FAILED(hr)) {
// Failure to Resolve: return empty/invalid shell item object
//
return TShellItem();
}
TPidl resolvedPidl;
hr = shellLink->GetIDList(resolvedPidl);
TXShell::Check(hr, IDS_LINKGETIDLISTFAIL);
// Construct the resolved TShellItem
//
TCreateStruct cs;
cs.Pidl = resolvedPidl.GetLastItem();
if (resolvedPidl.GetItemCount() == 1)
cs.ParentFolder = TShellItem(Desktop);
else {
// Expanded by Val Ovechkin 12:29 PM 6/3/98
void *p = STATIC_CAST(IShellFolder**, cs.ParentFolder);
hr = TShellItem(Desktop)->BindToObject(resolvedPidl.StripLastItem(), 0, IID_IShellFolder,
REINTERPRET_CAST(void **, p));
// hr = TShellItem(Desktop)->BindToObject(resolvedPidl.StripLastItem(), 0, IID_IShellFolder,
// REINTERPRET_CAST(void **, STATIC_CAST(IShellFolder**, cs.ParentFolder)));
TXShell::Check(hr, IDS_BINDTOOBJECTFAIL);
}
return TShellItem(cs);
}
//
/// Create a shortcut to a file object
///
/// \note IMPORTANT: This function calls CoCreateInstance. Therefore an
/// application that uses this function MUST have previously
/// called CoInitialize (or OLEInitialize).
//
HRESULT
TShellItem::CreateShortCut(LPCTSTR objPath, LPTSTR pathLink, LPTSTR description)
{
HRESULT hr;
TComRef<IShellLink> shellLink;
hr = TOle::CoCreateInstance(CLSID_ShellLink, 0, CLSCTX_INPROC_SERVER, IID_IShellLink,
REINTERPRET_CAST(void **, STATIC_CAST(IShellLink**, shellLink)));
TXShell::Check(hr, IDS_CREATESHELLLINKFAIL);
// Set path to shortcut target and add description
//
hr = shellLink->SetPath(TString(objPath));
TXShell::Check(hr, IDS_SHLLINKSETPATHFAIL);
hr = shellLink->SetDescription(TString(description));
TXShell::Check(hr, IDS_SHLLINKSETDESCFAIL);
// Query IShellLink for the IPersistFile Interface for saving the
// shortcut in persistent storage
//
TComRef<IPersistFile> persistFile;
hr = shellLink->QueryInterface(IID_IPersistFile,
REINTERPRET_CAST(void **, STATIC_CAST(IPersistFile**, persistFile)));
TXShell::Check(hr, IDS_LINKQUERYINTRFCFAIL);
// Save link by calling IPersistFile::Save
//
hr = persistFile->Save(TString(pathLink), TRUE);
return hr;
}
//
/// Create a shortcut to a non-file object
///
/// \note IMPORTANT: This function calls CoCreateInstance. Therefore an
/// application that uses this function MUST have previously
/// called CoInitialize (or OLEInitialize).
//
HRESULT
TShellItem::CreateShortCut(LPCITEMIDLIST pidl, LPTSTR pathLink, LPTSTR description)
{
HRESULT hr;
TComRef<IShellLink> shellLink;
hr = TOle::CoCreateInstance(CLSID_ShellLink, 0, CLSCTX_INPROC_SERVER, IID_IShellLink,
REINTERPRET_CAST(void **, STATIC_CAST(IShellLink**, shellLink)));
TXShell::Check(hr, IDS_CREATESHELLLINKFAIL);
// Set path to shortcut target and add description
//
shellLink->SetIDList(pidl);
shellLink->SetDescription(description);
// Query IShellLink for the IPersistFile Interface for saving the
// shortcut in persistent storage
//
TComRef<IPersistFile> persistFile;
hr = shellLink->QueryInterface(IID_IPersistFile,
REINTERPRET_CAST(void **, STATIC_CAST(IPersistFile**, persistFile)));
TXShell::Check(hr, IDS_LINKQUERYINTRFCFAIL);
// Save link by calling IPersistFile::Save
//
hr = persistFile->Save(TString(pathLink), TRUE);
return hr;
}
//
/// Creates a shortcut to this object
///
/// \note IMPORTANT: This function calls CoCreateInstance. Therefore an
/// application that uses this function MUST have previously
/// called CoInitialize (or OLEInitialize).
//
TShellItem
TShellItem::CreateShortCut(LPTSTR linkPath, LPTSTR description)
{
EnforceValidity();
if (IsPartOfFileSystem()) {
if (SUCCEEDED(CreateShortCut(GetPath(), linkPath, description)))
return TShellItem(linkPath);
} else {
if (SUCCEEDED(CreateShortCut(GetFullyQualifiedPidl(), linkPath,
description)))
return TShellItem(linkPath);
}
// Failure: return empty/invalid Shell Item object
//
return TShellItem();
}
//
/// Adds the TShellItem to the taskbar's recent document list
/// (see MS doc for more info on SHAddToRecentDocs,
/// Programmer's Guide to MS Windows 95, MS Press, p. 202)
//
void
TShellItem::AddToRecentDocs() const
{
EnforceValidity();
TShell::SHAddToRecentDocs(SHARD_PIDL, STATIC_CAST(const ITEMIDLIST *, GetFullyQualifiedPidl()));
}
//
/// CompareIDs is a protected function that compares a TShellItem's pidl with
/// another TShellItem's pidl
///
/// (see MS doc for more info CompareIDs,
/// Programmer's Guide to MS Windows 95, MS Press, p. 194)
//
short
TShellItem::CompareIDs(const TShellItem& rhs) const
{
EnforceValidity();
rhs.EnforceValidity();
HRESULT hr;
if (!HaveSameParent(rhs)) { // use the desktop
TShellItem desktop(Desktop);
hr = desktop->CompareIDs(0, GetFullyQualifiedPidl(), rhs.GetFullyQualifiedPidl());
WARNX(OwlShell, FAILED(hr), 0, _T("Desktop->CompareIDs failed.")
<< _T(" this TShellItem* = ") << hex << (LPVOID)this
<< _T(" other TShellItem* = ") << (LPVOID)&rhs);
TXShell::Check(hr, IDS_COMPAREIDSFAIL);
}
else {
hr = ParentFolder->CompareIDs(0, Pidl, rhs.Pidl);
WARNX(OwlShell, FAILED(hr), 0, _T("SameParent->CompareIDs failed.")
<< _T(" this TShellItem* = ") << hex << (LPVOID)this
<< _T(" other TShellItem* = ") << (LPVOID)&rhs);
TXShell::Check(hr, IDS_COMPAREIDSFAIL);
}
return STATIC_CAST(short, HRESULT_CODE(hr));
}
//
/// EnforceValidity is a protected function that checks that a TShellItem is valid
/// before proceeding with an operation that requires the TShellItem to be valid
//
void
TShellItem::EnforceValidity() const
{
WARNX(OwlShell, !Valid(), 0, _T("TShellItem is not valid.")
<< _T(" TShellItem* = ") << hex << (LPVOID)this);
if (!Valid())
TXShell::Raise(IDS_TSHELLITEMINVALID);
}
// Folder Functions
//
/// RetrieveIShellFolder is a protected function that checks to see if a TShellItem's
/// IShellFolder interface is already assigned a value. If not, it will BindToObject
/// in order to assign the interface a value. Note: Only TShellItem's that are
/// folders have an IShellFolder interface; consequently, if this function is
/// called on a TShellItem that is not a folder, an exception will be thrown.
//
void
TShellItem::RetrieveIShellFolder() const
{
EnforceValidity();
if (!I) { // Need to call BindToObject
IShellFolder* iface;
HRESULT hr = ParentFolder->BindToObject(Pidl, 0, IID_IShellFolder,
REINTERPRET_CAST(void **, &iface));
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::BindToObject failed.")
<< _T(" TShellItem* = ") << hex << (LPVOID)this << _T(" Parent IShellFolder* = ")
<< STATIC_CAST(IShellFolder*, *CONST_CAST(TComRef<IShellFolder>*, &ParentFolder)));
TXShell::Check(hr, IDS_BINDTOOBJECTFAIL);
// CONST_CAST(IShellFolder*, I) = iface;
CONST_CAST(TShellItem*,this)->I = iface;
}
}
//
/// EnumObjects is a protected function that returns an IEnumIDList enumerator on
/// a folder. Note: This function is only meaningful for folders; consequently,
/// if this function is called on a TShellItem that is not a folder, an
/// exception will be thrown.
//
void
TShellItem::EnumObjects(IEnumIDList** iface, HWND windowOwner,
const int kind) const
{
EnforceValidity();
RetrieveIShellFolder();
HRESULT hr = I->EnumObjects(windowOwner, (kind == -1)? TShellItemIterator::Folders |
TShellItemIterator::NonFolders: kind, iface);
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::EnumObjects failed.")
<< _T(" TShellItem* = ") << hex << (LPVOID)this);
TXShell::Check(hr, IDS_ENUMOBJECTSFAIL);
}
//
/// BrowseForFolder presents a dialog box to the user in which he can select a
/// subfolder. The root of the dialog box is this TShellItem folder.
///
/// \note It is only meaningful to call this function if the TShellItem is a
/// folder; consequently, if this function is called on a TShellItem that is
/// not a folder, an exception will be thrown.
///
/// Additional Documentation: See MS doc on SHBrowseForFolder for more info,
/// more info, Programmer's Guide to MS Windows 95, MS Press, pp. 202-203.
//
bool
TShellItem::BrowseForFolder(TCreateStruct& cs, HWND windowOwner,
const tchar* title, const UINT flags, int* image,
const bool includeStatus, BFFCALLBACK func,
const LPARAM param) const
{
EnforceValidity();
RetrieveIShellFolder();
tchar displayName[_MAX_PATH];
BROWSEINFO bi;
bi.hwndOwner = windowOwner;
TPidl fullPidl = GetFullyQualifiedPidl();
bi.pidlRoot = fullPidl;
bi.pszDisplayName = displayName;
bi.lpszTitle = title;
bi.ulFlags = flags;
if (includeStatus)
bi.ulFlags |= BIF_STATUSTEXT;
bi.lpfn = func;
bi.lParam = param;
TPidl pidl = TShell::SHBrowseForFolder(&bi);
if (!pidl)
return false;
else {
if (image)
*image = bi.iImage;
TShellItem desktop(Desktop);
cs.Pidl = pidl.GetLastItem();
// Expanded by Val Ovechkin 12:29 PM 6/3/98
void *p = STATIC_CAST(IShellFolder**, cs.ParentFolder);
HRESULT hr = desktop->BindToObject(pidl.StripLastItem(), 0, IID_IShellFolder
,
REINTERPRET_CAST(void **, p));
// HRESULT hr = desktop->BindToObject(pidl.StripLastItem(), 0, IID_IShellFolder,
// REINTERPRET_CAST(void **, STATIC_CAST(IShellFolder**, cs.ParentFolder)));
WARNX(OwlShell, FAILED(hr), 0, _T("IShellFolder::BindToObject failed.")
<< _T(" TShellItem* = ") << hex << (LPVOID)this
<< _T(" Desktop IShellFolder* = ") << STATIC_CAST(IShellFolder*, desktop));
TXShell::Check(hr, IDS_BINDTOOBJECTFAIL);
return true;
}
}
//
/// ParseDisplayName parses a "for parsing" display name into a TCreateStruct
/// (which can be used to create a TShellItem). In general, it is not necessary
/// to call this function when using OWL's shell classes.
///
/// \note It is only meaningful to call this function if the TShellItem is a
/// folder; consequently, if this function is called on a TShellItem that is
/// not a folder, an exception will be thrown.
///
/// Additional Documentation: See MS doc on IShellFolder::ParseDisplayName for
/// more info, Programmer's Guide to MS Windows 95, MS Press, pp. 191-192.
//
HRESULT
TShellItem::ParseDisplayName(TCreateStruct& cs, const tchar* displayName,
ulong* eaten, HWND windowOwner,
ulong* attrib) const
{
EnforceValidity();
TPidl pidl;
ulong charsEaten;
ulong attributes;
RetrieveIShellFolder();
HRESULT hr = I->ParseDisplayName(windowOwner, 0, TString(displayName),
&charsEaten, pidl, &attributes);
if (hr == NOERROR) {
if (eaten)
*eaten = charsEaten;
if (attrib)
*attrib = attributes;
cs.Pidl = pidl;
cs.ParentFolder = *this;
}
return hr;
}
//
/// TShellItemIterator constructor.
///
/// \note It is only meaningful to construct iterators on folders; therefore,
/// if this constructor is passed a TShellItem that is not a folder, an exception
/// will be thrown.
///
/// Additional Documentation: See MS doc on IEnumIDList for more info,
/// Programmer's Guide to MS Windows 95, MS Press, pp. 199-201.
//
TShellItemIterator::TShellItemIterator(const TShellItem& folder, HWND windowOwner,
const int kind)
:
Index(0),
Pidl(0),
Folder(folder)
{
folder.EnumObjects(*this, windowOwner, kind);
Folder = folder; // even though filled in above, the IShellFolder* may not have
// yet have been filled in. (See RetrieveIShellFolder)
Next();
}
//
/// TShellItemIterator copy constructor.
///
/// \note This function does not work with the intial release of Win95 because
/// Win95's IEnumIDList::Clone is broken (it returns E_FAIL (unspecified error))
//
TShellItemIterator::TShellItemIterator(const TShellItemIterator& source)
:
Index(source.Index),
Pidl(source.Pidl),
Folder(source.Folder)
{
if (!source.I)
I = 0;
else {
HRESULT hr = source.I->Clone(*this);
WARNX(OwlShell, FAILED(hr), 0, _T("IEnumIDList::Clone failed.")
<< _T(" TShellItemIterator* = ") << hex << (LPVOID)this
<< _T(" source IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, CONST_CAST(TShellItemIterator&, source)));
TXShell::Check(hr, IDS_CLONEFAIL);
}
}
//
/// TShellItemIterator assignment operator
///
/// \note This function does not work with the intial release of Win95 because
/// Win95's IEnumIDList::Clone is broken (it returns E_FAIL (unspecified error))
//
TShellItemIterator& TShellItemIterator::operator = (const TShellItemIterator& source)
{
if (&source != this) {
if (!source.I)
I = 0;
else {
HRESULT hr = source.I->Clone(*this);
WARNX(OwlShell, FAILED(hr), 0, _T("IEnumIDList::Clone failed.")
<< _T(" TShellItemIterator* = ") << hex << (LPVOID)this << _T(" source IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, CONST_CAST(TShellItemIterator&, source)));
TXShell::Check(hr, IDS_CLONEFAIL);
Index = source.Index;
Pidl = source.Pidl;
Folder = source.Folder;
}
}
return *this;
}
//
/// operator IEnumIDList** resets the interface pointer and returns a pointer
/// to this interface pointer.
//
TShellItemIterator::operator IEnumIDList**()
{
return TComRef<IEnumIDList>::operator IEnumIDList**();
}
//
/// operator ++ increments the "cursor" in the iterator, then returns
/// the item pointed to by the cursor
///
/// The item is returned as a TShellItem::TCreateStruct structure. For more
/// information about TCreateStruct, see the comments in the
/// TShellItem::TShellItem(const TCreateStruct& cs) constructor.
//
TShellItem::TCreateStruct TShellItemIterator::operator ++()
{
EnforceValidInterface();
return operator[] (Index + 1);
}
//
/// operator ++(int) returns the item pointed to by the "cursor," then increments
/// the cursor.
///
/// The item is returned as a TShellItem::TCreateStruct structure. For more
/// information about TCreateStruct, see the comments in the
/// TShellItem::TShellItem(const TCreateStruct& cs) constructor.
//
TShellItem::TCreateStruct
TShellItemIterator::operator ++(int)
{
EnforceValidInterface();
WARNX(OwlShell, Index == -1, 0, _T("Attempt to read past end of TShellItemIterator")
<< _T(" TShellItemIterator* = ") << hex << (LPVOID)this << _T(" IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, *CONST_CAST(TShellItemIterator*, this)));
if (Index == -1)
TXShell::Raise(IDS_SHELLITERATORATEND);
TShellItem::TCreateStruct cs(Pidl, Folder);
Next();
if (Index != -1)
Index++;
return cs;
}
//
/// operator -- decrements the "cursor" in the iterator, then returns
/// the item pointed to by the cursor
///
/// The item is returned as a TShellItem::TCreateStruct structure. For more
/// information about TCreateStruct, see the comments in the
/// TShellItem::TShellItem(const TCreateStruct& cs) constructor.
///
/// \note This function does not work with the intial release of Win95 because
/// it requires IEnumIDList::Reset to be called and IEnumIDList::Reset returns
/// E_NOTIMPL (not implemented) in the intitial Win95 release.
//
TShellItem::TCreateStruct
TShellItemIterator::operator --()
{
EnforceValidInterface();
return operator[] (Index - 1);
}
//
/// operator --(int) returns the item pointed to by the "cursor," then decrements
/// the cursor.
///
/// The item is returned as a TShellItem::TCreateStruct structure. For more
/// information about TCreateStruct, see the comments in the
/// TShellItem::TShellItem(const TCreateStruct& cs) constructor.
///
/// \note This function does not work with the intial release of Win95 because
/// it requires IEnumIDList::Reset to be called and IEnumIDList::Reset returns
/// E_NOTIMPL (not implemented) in the intitial Win95 release.
//
TShellItem::TCreateStruct
TShellItemIterator::operator --(int)
{
EnforceValidInterface();
WARNX(OwlShell, Index == -1, 0, _T("Attempt to read past end of TShellItemIterator")
<< _T(" TShellItemIterator* = ") << hex << (LPVOID)this << _T(" IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, *CONST_CAST(TShellItemIterator*, this)));
if (Index == -1)
TXShell::Raise(IDS_SHELLITERATORATEND);
TShellItem::TCreateStruct cs(Pidl, Folder);
Reset();
Skip(--Index);
Next();
return cs;
}
//
/// operator Current returns the item pointed to by the "cursor"
///
/// The item is returned as a TShellItem::TCreateStruct structure. For more
/// information about TCreateStruct, see the comments in the
/// TShellItem::TShellItem(const TCreateStruct& cs) constructor.
//
TShellItem::TCreateStruct
TShellItemIterator::Current()
{
EnforceValidInterface();
WARNX(OwlShell, Index == -1, 0, _T("Attempt to read past end of TShellItemIterator")
<< _T(" TShellItemIterator* = ") << hex << (LPVOID)this << _T(" IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, *CONST_CAST(TShellItemIterator*, this)));
if (Index == -1)
TXShell::Raise(IDS_SHELLITERATORATEND);
return TShellItem::TCreateStruct(Pidl, Folder);
}
//
/// Next is a protected function that increments the cursor. Equivalent to Skip(1).
//
void TShellItemIterator::Next()
{
EnforceValidInterface();
WARNX(OwlShell, Index == -1, 0, _T("Attempt to read past end of TShellItemIterator")
<< _T(" TShellItemIterator* = ") << hex << (LPVOID)this << _T(" IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, *CONST_CAST(TShellItemIterator*, this)));
if (Index == -1)
TXShell::Raise(IDS_SHELLITERATORATEND);
HRESULT hr = I->Next(1, Pidl, 0);
WARNX(OwlShell, FAILED(hr), 0, _T("IShellItemIterator::Next failed.")
<< _T(" TShellItemIterator* = ") << hex << (LPVOID)this << _T(" IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, *CONST_CAST(TShellItemIterator*, this)));
TXShell::Check(hr, IDS_IDLISTNEXTFAIL);
if (hr == S_FALSE)
Index = -1;
}
//
/// Skip advances the cursor \<count\> times. Equivalent to calling Next \<count\>
/// times.
///
/// \note This function does not work with the intial release of Win95 because
/// IEnumIDList::Skip returns E_NOTIMPL (not implemented) in the intitial Win95
/// release.
//
void
TShellItemIterator::Skip(const ulong count)
{
EnforceValidInterface();
if (count) {
HRESULT hr = I->Skip(count);
WARNX(OwlShell, FAILED(hr), 0, _T("IShellItemIterator::Skip failed.")
<< _T(" TShellItemIterator* = ") << hex << (LPVOID)this << _T(" IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, *CONST_CAST(TShellItemIterator*, this))
<< _T(" count = ") << count);
TXShell::Check(hr, IDS_IDLISTSKIPFAIL);
if (hr == S_FALSE)
Index = -1;
else
Index += count;
}
}
//
/// Reset resets the cursor to the beginning.
///
/// \note This function does not work with the intial release of Win95 because
/// IEnumIDList::Reset returns E_NOTIMPL (not implemented) in the intitial Win95
/// release.
//
void
TShellItemIterator::Reset()
{
EnforceValidInterface();
HRESULT hr = I->Reset();
WARNX(OwlShell, FAILED(hr), 0, _T("IShellItemIterator::Reset failed.")
<< _T(" TShellItemIterator* = ") << hex << this << _T(" IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, *CONST_CAST(TShellItemIterator*, this)));
TXShell::Check(hr, IDS_IDLISTRESETFAIL);
Index = 0;
}
//
/// operator [] returns the item at the \<index\> location. index is zero based.
///
/// The item is returned as a TShellItem::TCreateStruct structure. For more
/// information about TCreateStruct, see the comments in the
/// TShellItem::TShellItem(const TCreateStruct& cs) constructor.
///
/// \note operator[] doesn't work with the intial release of Win95 because it
/// calls Skip and Reset wich are broken in the intial Win95 release. Both of
/// these functions return E_NOTIMPL (not implemented). The only way operator[]
/// will work is to call it sequentially, beginning at index 0 (i.e., to use it
/// like operator ++().
//
TShellItem::TCreateStruct
TShellItemIterator::operator [](const long index)
{
EnforceValidInterface();
if (Index == index)
return TShellItem::TCreateStruct(Pidl, Folder);
if (Index < index)
Skip(index - Index - 1);
else if (Index > index) {
Reset();
Skip(index);
}
Next();
WARNX(OwlShell, Index == -1, 0, _T("Attempt to read past end of TShellItemIterator")
<< _T(" TShellItemIterator* = ") << hex << this << _T(" IEnumIDList* = ")
<< STATIC_CAST(IEnumIDList*, *CONST_CAST(TShellItemIterator*, this)));
if (Index == -1) {
TXShell::Raise(IDS_SHELLITERATORATEND);
return TShellItem::TCreateStruct(); // To satisfy compiler
}
else
return TShellItem::TCreateStruct(Pidl, Folder);
}
//
/// GetCount returns the number of items in the iterator's list
///
/// \note This function does not work with the intial release of Win95 because
/// Clone and Reset are borken in the intitial Win95 release.
//
long
TShellItemIterator::GetCount() const
{
EnforceValidInterface();
TShellItemIterator list(*this); // clone so we don't affect position
list.Reset();
if (list.Index == -1)
return 0;
long cnt = 1;
while (list.Skip(1), Index != -1)
cnt++;
return cnt;
}
//
/// EnforceValidInterface is a protected function that checks to see that this
/// TShellItemIterator contains a valid IEnumIDList interface pointer. If it
/// doesn't, it throws an exception.
//
void
TShellItemIterator::EnforceValidInterface() const
{
WARNX(OwlShell, !I, 0, _T("TShellItemIterator is not valid (IEnumIDList* == 0).")
<< _T(" TShellItemIterator* = ") << hex
<< STATIC_CAST(IEnumIDList*, *CONST_CAST(TShellItemIterator*, this)));
if (!I) {
TXShell::Raise(IDS_IDLISTZEROPOINTER);
}
}
//
/// Constructs a TXShell object with a default IDS_SHELLFAILURE message.
//
TXShell::TXShell(uint resId, HANDLE handle)
:
TXOwl(MakeMessage(resId, static_cast<uint>(reinterpret_cast<UINT_PTR>(handle))), resId)
{
}
TXShell* TXShell::Clone()
{
return new TXShell(*this);
}
//
/// Throws the exception object.
//
void TXShell::Throw()
{
throw *this;
}
//
/// Constructs a TXShell exception from scratch, and throws it.
//
void TXShell::Raise(uint resId, HANDLE handle)
{
TXShell(resId, handle).Throw();
}
//
/// Checks an HResult and throws a TXShell if not SUCCEEDED(hr).
//
void TXShell::Check(HRESULT hr, uint resID, HANDLE handle)
{
if FAILED(hr)
Raise(resID, handle);
}
} // OWL namespace
/* ========================================================================== */
↑ V526 The 'strncmp' function returns 0 if corresponding strings are equal. Consider examining the condition for mistakes.
↑ V820 The 'pidl' variable is not used after copying. Copying can be replaced with move/swap for optimization.
↑ V820 The 'pidl' variable is not used after copying. Copying can be replaced with move/swap for optimization.