Componentes.Terceros.Mustan.../official/1.7.0/Common Library/Source/MPShellUtilities.pas
david 778b05bf9f Importación inicial
- Mustangpeak Common Library - 1.7.0
  - EasyListview - 1.7.0

git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.Mustangpeak@2 60b41242-d4b9-2247-b156-4ccd40706241
2007-09-11 08:33:06 +00:00

9152 lines
340 KiB
ObjectPascal
Raw Blame History

unit MPShellUtilities;
// Version 1.7.0
//
// The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
// Alternatively, you may redistribute this library, use and/or modify it under the terms of the
// GNU Lesser General Public License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any later version.
// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/.
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
// specific language governing rights and limitations under the License.
//
// The initial developer of this code is Jim Kueneman <jimdk@mindspring.com>
//
//----------------------------------------------------------------------------
//
// Credits for valuable information and code gathered from newsgroups and
// websites:
// Angus Johnson for his GetDiskFreeSpaceFAT32 function from UNDO
//
//----------------------------------------------------------------------------
interface
{$I ..\Include\Addins.inc}
{.$DEFINE GXDEBUG_DEFMENUCREATE_CALLBACK}
{.$DEFINE GXDEBUG_VIRTUALCONTEXTMENU}
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
{$DEFINE GX_DEBUG}
{$ENDIF}
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
{$DEFINE GX_DEBUG}
{$ENDIF}
{$B-}
{$include Compilers.inc}
{$include Options.inc}
uses
{$IFDEF GX_DEBUG}
DbugIntf,
{$ENDIF}
Windows,
Messages,
SysUtils,
Classes,
Graphics,
Controls,
Forms,
ImgList,
ShlObj,
ShellAPI,
ActiveX,
Registry,
MPShellTypes,
MPCommonObjects,
MPCommonUtilities,
MPThreadManager,
MPResources,
MPDataObject,
{$IFDEF TNTSUPPORT}
TntSysUtils,
TntClasses,
TntRegistry,
TntMenus,
{$ENDIF}
{$IFDEF COMPILER_6_UP}
Variants,
{$ENDIF}
{$IFDEF COMPILER_5_UP}
Contnrs,
{$ENDIF}
Menus;
const
DefaultDetailColumns = 5;
ID_TIMER_NOTIFY = 100;
ID_TIMER_ENUMBKGND = 101;
ID_TIMER_AUTOSCROLL = 102;
ID_TIMER_SHELLNOTIFY = 103;
SHORTCUT_ICON_INDEX = 29; // This is cheezy crappy stupid and dumb but
// I can't find a way to get the link index
SHELL_NAMESPACE_ID = -1; // ID of a basic Shell Namespace based TNamespace
SHGDN_FOREDITING = $1000;
DEFAULTPIDLARRAYSIZE = 8192; // Default size for the TPIDLArray
STREAM_VERSION_DEFAULT = -1; // Default Stream version for TStreamableClass.LoadFromStream
// if this value is seen the LoadFromStream method should read the version
// from the stream else it should use the passed version in the method
STR_IMAGE_THREAD_EVENT = 'jdkImageThreadEvent';
VET_DEFAULT_COLUMNWIDTHS: array[0..36] of integer = (
180, // Name
96, // Size
120, // Type
120, // Modified
60, // Attributes
180, // Comment
120, // Created
120, // Accessed
120, // Owner
120, // Author
120, // Title
120, // Subject
120, // Catagory
60, // Pages
120, // Copywrite
120, // Company Name
120, // Module Description
120, // Module Version
120, // Product Name
120, // Product Version
72, // Sender Name
90, // Recipient Name
102, // Recipient Number
30, // Csid
30, // Tsid
108, // Transmission Time
60, // Caller ID
48, // Routing
180, // Audio Format
180, // Sample Rate
180, // Audio Sample Size
180, // Channels
180, // Play Length
180, // Frame Count
180, // Frame Rate
180, // Video Sample Size
180 // Video Compression
);
VET_DEFAULT_DRIVES_COLUMNWIDTHS: array[0..3] of integer = (
180, // Name
120, // Type
96, // Total Size
96 // Free Space
);
VET_DEFAULT_CONTROLPANEL_COLUMNWIDTHS: array[0..1] of integer = (
180, // Name
300 // Description
);
VET_DEFAULT_NETWORK_COLUMNWIDTHS: array[0..1] of integer = (
180, // Name
300 // Description
);
{-------------------------------------------------------------------------------}
{ Custom enumerated types }
{-------------------------------------------------------------------------------}
type
TDefaultFolderIcon = (
diNormalFolder, // Retrieve the index for a normal file folder icon
diOpenFolder, // Retrieve the index for a normal file folder icon in the open state
diUnknownFile, // Retrieve the index for an icon for a file that has no association
diLink, // Retrieve the index for link overlay icon
diMyDocuments // Index of MyDocuments icon
);
TMPBrowseFlag = (
mpbfComputers, // Only allow computers to be returned
mpbfPrinters, // Only allow printers to be returned
mpbfDontGoBelowDomain, // Don't allow browsing into other network domains
mpbfFileSysAncestors, // Only allow File system ancestors to be returned
mpbfFileSysFolder, // Only allow file system folders to be returned
// mpbfIncludeStatusText, // Includes status test in the dialog
mpbfIncludeFiles, // Include files
mpbfNewStyleDialog, // New style dialog
mpbfEditBox, // Add a edtibox for the use to type into
mpbfIncludeURLs, // Include URLs
mpbfSharable, // sharable folders
mpbfMustExist // Returned folder must exist or cancel pressed before dialog will close
);
TMPBrowseFlags = set of TMPBrowseFlag;
type
TNamespaceState = (
nsFreePIDLOnDestroy, // If true the object free the PIDL with the system allocator when freed
nsIsRecycleBin, // Recyclebin does not cooperate very well so we have do extra checks for various reasons so cache this
nsRecycleBinChecked, // Flag to see if above is valid.
nsOwnsParent, // If a namespace is created from a complex PIDL for some methods the parent is needed. If so a Parent namespace is created so this instance owns it and must free it.
nsShellDetailsSupported, // Instead of a costly call to see if interface exists when it does not we only check once then cache the result
nsShellFolder2Supported, // Same idea as ShellDetailsSupported, if it does not exist don't waste time constantly checking
nsShellOverlaySupported, // Same idea as ShellDetailsSupported, if it does not exist don't waste time constantly checking
nsThreadedIconLoaded, // To keep the threaded icon option fast and interuptable need to track if a thread is currently trying to extract the namespace icon index. Retrieved.
nsThreadedIconLoading, // To keep the threaded icon option fast and interuptable need to track if a thread is currently trying to extract the namespace icon index. In queue.
nsThreadedImageLoaded, // To keep the threaded image option fast and interuptable need to track if a thread is currently trying to extract the namespace thumbnail. Retrieved.
nsThreadedImageLoading, // To keep the threaded image option fast and interuptable need to track if a thread is currently trying to extract the namespace thumbnail. In queue.
nsThreadedImageResizing, // To keep the threaded image option fast and interuptable need to track if a thread is currently trying to resize the namespace thumbnail. In queue.
nsThreadedTileInfoLoaded, // To keep the threaded Tile Info option fast and interuptable need to track if a thread is currently trying to extract Tile Info for the namespace, Threaded Tile Info is retrieved
nsThreadedTileInfoLoading, // To keeping the threaded Tile Info option fast and interuptable need to track if a thread is currently trying to extract Tile Info for the namespace, it is in the queue to be retieved
nsIconIndexChanged // Sets a flag to track when the IconIndex changed between calls to GetIconIndex. Usually caused by a Thread Setting the icon index
);
TNamespaceStates = set of TNamespaceState;
type
// {$HPPEMIT 'class TNamespace;'}
TSHLimitInputEdit = function(hWndEdit: HWND; psf: IShellFolder): HRESULT; stdcall;
{$IFNDEF CPPB_6_UP}
IVETShellDetails = IShellDetails;
{$ELSE}
IVETShellDetails = IShellDetailsBCB6;
{$ENDIF}
TShellCache = set of ( // Valid entries in the Namespace data cache
scInFolderName,
scNormalName,
scParsedName,
scSmallIcon,
scSmallOpenIcon,
scOverlayIndex,
scCreationTime,
scLastAccessTime,
scLastWriteTime,
scFileSize,
scFileSizeKB,
scFileSizeInt64,
scFileType,
scInvalidIDListData, // If SHGetDataFromIDList fails flag it so we won't try again.
scFileSystem,
scFolder,
scCanDelete,
scCanRename,
scGhosted,
scCanCopy,
scCanMove,
scCanLink,
scLink,
scFileSysAncestor,
scCompressed,
scFileTimes,
scSupportedColumns,
scFolderSize, // Recursivly calculated size of folder contents
scVirtualHook, // Namespace is the invisible parent of the RootVirtualNamespace
scHookedNamespace, // Namespace has custom sub items injected through SubFolderHook property
scVirtualNamespace, // Namespace is a virtual namespace
scRootVirtualNamespace, // Namespace is the Root of the Custom Namespace Branch. If a real NS is hooked it is the namespace under the caSubItemHook namespace
scHardHookedNamespace, // Namespace is a hard hooked, this means it will show user defined details if defined instead of using the real namespaces parent to create the details
scDetailsOfCache,
scBrowsable
);
{ This stores the state of the cached folder attribute. }
TCacheAttributes = set of (
caFileSystem, // Namespace is part of the file system
caFolder, // Namespace is a Folder (not necessarily a directory)
caCanDelete, // Namespace can be deleted
caCanRename, // Namespace can be renamed
caGhosted, // Namespace is should display a ghosted icon
caCanCopy, // Namespace can be copied
caCanMove, // Namespace can be moved to a different location
caCanLink, // Namespace can create a link to itself
caLink, // Namespace *is* a link
caFileSysAncestor, // Namespace is an ancestor of a file system namespace
caCompressed, // Namespace represents a compressed folder
caVirtualHook, // Namespace is the invisible parent of the RootVirtualNamespace
caHookedNamespace, // Namespace has custom sub items injected through SubFolderHook property
caVirtualNamespace, // Namespace is a virtual namespace
caRootVirtualNamespace, // Namespace is the Root of the Custom Namespace Branch. If a real NS is hooked it is the namespace under the caSubItemHook namespace
caHardHookedNamespace, // Namespace is a hard hooked, this means it will show user defined details if defined instead of using the real namespaces parent to create the details
caBrowsable
);
{ Used in IShellFolder2.GetDefaultColumnState }
TSHColumnState = (
csTypeString, // A string.
csTypeInt, // An integer.
csTypeDate, // A date.
csOnByDefault, // Should be shown by default in the Microsoft<66> Windows<77> Explorer Details view.
csSlow, // Extracting information about the column can be time consuming.
csExtended, // Provided by a handler, not the folder object.
csSecondaryUI, // Not displayed in the context menu, but listed in the More dialog box.
csHidden // Not displayed in the user interface.
);
TSHColumnStates = set of TSHColumnState;
THotKeyModifier = ( // For IShellLink
hkmAlt, // HOTKEYF_ALT
hkmControl, // HOTKEYF_CONTROL
hkmExtendedKey, // HOTKEYF_EXT
hkmShift // HOTKEYF_SHIFT
);
THotKeyModifiers = set of THotKeyModifier;
TCmdShow = ( // For IShellLink
swHide, // Hides the window and activates another window.
swMaximize, // Maximizes the specified window.
swMinimize, // Minimizes the specified window and activates the next top-level window in the Z order.
swRestore, // Activates and displays the window. If the window is minimized or maximized, Windows restores it to its original size and position. An application should specify this flag when restoring a minimized window.
swShow, // Activates the window and displays it in its current size and position.
swShowDefault, // Sets the show state based on the SW_ flag specified in the STARTUPINFO structure passed to the CreateProcess function by the program that started the application.
swShowMinimized, // Activates the window and displays it as a minimized window.
swShowMinNoActive, // Displays the window as a minimized window. The active window remains active.
swShowNA, // Displays the window in its current state. The active window remains active.
swShowNoActive, // Displays a window in its most recent size and position. The active window remains active.
swShowNormal // Activates and displays a window. If the window is minimized or maximized, Windows
); // restores it to its original size and position. An application should specify this flag
// when displaying the window for the first time.
TIconSize = (
icSmall, // Small Shell size icon, usually 16x16
icLarge // Large TListview size Icon, usually 32x32
);
TFileSort = ( // Used in the ShellSortHelper class
fsFileType, // Sort by the File Type name
fsFileExtension // Sort by the file extenstion
);
TObjectDescription = ( // Return from SHGetDataFromIDList with SHGDFIL_DESCRIPTIONID param
odError, // The call Failed for some reason
odRootRegistered, // The item is a registered item on the desktop.
odFile, // The item is a file.
odDirectory, // The item is a folder.
odUnidentifiedFileItem, // The item is an unidentified item in the file system.
od35Floppy, // The item is a 3.5-inch floppy drive.
od525Floppy, // The item is a 5.25-inch floppy drive.
odRemovableDisk, // The item is a removable disk drive.
odFixedDrive, // The item is a fixed disk drive.
odMappedDrive, // The item is a drive that is mapped to a network share.
odCDROMDrive, // The item is a CD-ROM drive.
odRAMDisk, // The item is a RAM disk.
odUnidentifiedDevice, // The item is an unidentified system device.
odNetworkDomain, // The item is a network domain.
odNetworkServer, // The item is a network server.
odNetworkShare, // The item is a network share.
odNetworkRestOfNet, // Not currently used.
odUnidentifiedNetwork, // The item is an unidentified network resource.
odComputerImaging, // Not currently used.
odComputerAudio, // Not currently used.
odShareDocuments // The item is the system shared documents folder.
);
TDetailsColumnTitleInfo = (
tiCenterAlign, // The header title is Center Aligned
tiLeftAlign, // The header title is Left Aligned
tiRightAlign, // The header title is Right Aligned
tiContainsImage // The header title is Contains an Image (were do you get the image???)
);
{ Selects what type of namespaces are enumerated and displayed in VET. }
TFileObjects = set of (
foFolders,
foNonFolders,
foHidden,
foShareable,
foNetworkPrinters
);
TSHColumnIDArray = array of TSHColumnID;
TGUIDArray = array of TGUID;
TWideStringArray = array of WideString;
TCategoryArray = array of ICategorizer;
TCategoryInfo = record
Description: WideString;
Collapsed: Boolean;
Hidden: Boolean;
end;
TCategoryInfoArray = array of TCategoryInfo;
TBooleanArray = array of Boolean;
TCategoriesInfo = record
ColumnID: TSHColumnIDArray;
CatGUID: TGUIDArray;
CategoryNames: TWideStringArray;
Categories: TCategoryInfoArray;
CanCatatorize: TBooleanArray;
DefaultColumn: Integer; // Index into above arrays to the default grouping column
CategoryCount: Integer;
end;
TBtyeSize = (
bsKiloBytes,
bsMegaBytes,
bsGigiBytes,
bsTereBytes,
bsCustom
);
{-------------------------------------------------------------------------------}
{-------------------------------------------------------------------------------}
{ Custom Data structures }
{-------------------------------------------------------------------------------}
type
TNamespace = class; // Forward
TExtractImage = class; // Forward
TCommonShellContextMenu = class; // Forward
TMenuItemIDArray = array of cardinal;
TVisibleColumnIndexArray = array of Word; // Array of the column indexes that are currently visible for a namespace
{ Array that contains the cached information for the folder. }
TDetailsOfCacheFlag = (
docCaptionValid,
docStatesValid,
docThreadLoading,
docThreadLoaded
);
TDetailsOfCacheFlags = set of TDetailsOfCacheFlag;
PDetailsOfCacheRec = ^TDetailsOfCacheRec;
TDetailsOfCacheRec = packed record
Cached: TDetailsOfCacheFlags;
Caption: WideString;
States: TSHColumnStates;
end;
TDetailsOfCacheArray = array of TDetailsOfCacheRec;
TCacheData = packed record
Attributes: TCacheAttributes; // Boolean attributes for the namespace are saved as bits
SmallIcon, // Index in the ShellImageList of the normal icon
SmallOpenIcon: integer; // Index in the ShellImageList of the open or selected icon
InFolderName, // InFolder display name for the namespace
NormalName, // Normal display name for the namespace
ParsedName, // The Path of the namespace if it is a file object, if not it is usually the same as NameNormal
CreationTime, // String of the object creation time in details mode
LastAccessTime, // String of the last accessed time in details mode
LastWriteTime, // String of the last write time in details mode
FileSize, // String of the file size "23,0000"
FileSizeKB, // String of the file size ala Explorer style i.e. "23 KB"
FileType: WideString; // @@@@ FileType shown in Explorer details mode
FileSizeInt64: Int64; // Actual File Size
SupportedColumns: integer; // Number of supported columns in details mode
FolderSize: Int64; // Recursivly calcuated size of folder contents
OverlayIndex, // Cache the Index of the Overlay
OverlayIconIndex: Integer; // Cache the Index of the Overlay Icon
DetailsOfCache: TDetailsOfCacheArray // Cached strings for report view
end;
{ Cache record tracks which information in the Data structure is valid with the }
{ ShellCacheFlags. }
TShellCacheRec = packed record
ShellCacheFlags: TShellCache; // If flag is set the corresponding data stored in Data is valid
Data: TCacheData; // Cached data for fast retrieval
end;
PSHGetFileInfoRec = ^TSHGetFileInfoRec;
TSHGetFileInfoRec = packed record
FileType: WideString; // Holds the File Type column detail if not using ShellColumns (using VET or custom columns)
end;
TShellContextMenuFlag = (
cmfCanRename, // This flag is set if the calling application supports renaming of items. A shortcut menu extension or drag-and-drop handler should ignore this flag. A namespace extension should add a rename item to the menu if applicable.
cmfDefaultOnly, // This flag is set when the user is activating the default action, typically by double-clicking. This flag provides a hint for the shortcut menu extension to add nothing if it does not modify the default item in the menu. A shortcut menu extension or drag-and-drop handler should not add any menu items if this value is specified. A namespace extension should add only the default item (if any).
// cmfDisableVerbs, // Microsoft Windows Vista and later: This flag is set when the calling application wants to invoke verbs that are otherwise disabled, such as legacy menus.
cmfExplore, // This flag is set when the Microsoft Windows Explorer tree window is present.
cmfExtendedVerbs, // This flag is set when the calling application wants extended verbs. Normal verbs are displayed when the user right-clicks an object. To display extended verbs, the user must right-click while pressing the SHIFT key.
cmfIncludeStatic, // Deprecated, do not use.
cmfItemMenu, // Microsoft Windows Vista and later: This flag is set when the calling application is invoking a context menu on an item in the view (as opposed to the background of the view).
cmfNoDefault, // This flag is set if no item in the menu has been set as the default. A drag-and-drop handler should ignore this flag. A namespace extension should not set any of the menu items to the default.
cmfNormal, // Indicates normal operation. A shortcut menu extension, namespace extension, or drag-and-drop handler can add all menu items.
cmfNoVerbs, // This flag is set for items displayed in the Send To menu. Shortcut menu handlers should ignore this value.
cmfVerbsOnly // This flag is set if the shortcut menu is for a shortcut object. Shortcut menu handlers should ignore this value.
);
TShellContextMenuFlags = set of TShellContextMenuFlag;
TNamespaceArray = array of TNamespace;
TCommonShellMenuEvent = procedure(Sender: TCommonShellContextMenu) of object;
{$IFDEF TNTSUPPORT}
TCommonShellMenuInvokeEvent = procedure(Sender: TCommonShellContextMenu; MenuItem: TTntMenuItem; InvokeInfo: PCMInvokeCommandInfo; var Handled: Boolean) of object;
{$ELSE}
TCommonShellMenuInvokeEvent = procedure(Sender: TCommonShellContextMenu; MenuItem: TMenuItem; InvokeInfo: PCMInvokeCommandInfo; var Handled: Boolean) of object;
{$ENDIF}
TCommonShellMenuMergeEvent = procedure(Sender: TCommonShellContextMenu; Menu: HMENU; IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags) of object;
TCommonShellMenuItemEvent = procedure(Sender: TCommonShellContextMenu; ShellFolder: IShellFolder; DataObject: IDataObject; var Handled: Boolean) of object;
TCommonShellMenuNewItemEvent = procedure(Sender: TCommonShellContextMenu; NS: TNamespace) of object;
TCommonShellMenuCopyEvent = TCommonShellMenuItemEvent;
TCommonShellMenuCreateShortcutEvent = TCommonShellMenuItemEvent;
TCommonShellMenuCutEvent = TCommonShellMenuItemEvent;
TCommonShellMenuDeleteEvent = TCommonShellMenuItemEvent;
TCommonShellMenuNewFolderEvent = TCommonShellMenuItemEvent;
TCommonShellMenuPasteEvent = TCommonShellMenuItemEvent;
TCommonShellMenuPasteLinkEvent = TCommonShellMenuItemEvent;
TCommonShellMenuPasteShortCutEvent = TCommonShellMenuItemEvent;
TCommonShellMenuProperitesEvent = TCommonShellMenuItemEvent;
{-------------------------------------------------------------------------------}
// Custom Exceptions
EVSTInvalidFileName = class(Exception)
end;
{-------------------------------------------------------------------------------}
{ Persistent Storing and Recreating VET }
{-------------------------------------------------------------------------------}
TStreamableClass = class(TPersistent)
private
FStreamVersion: integer;
public
constructor Create;
procedure LoadFromFile(FileName: WideString; Version: integer = 0; ReadVerFromStream: Boolean = False); virtual;
procedure LoadFromStream(S: TStream; Version: integer = 0; ReadVerFromStream: Boolean = False); virtual;
procedure SaveToFile(FileName: WideString; Version: integer = 0; ReadVerFromStream: Boolean = False); virtual;
procedure SaveToStream(S: TStream; Version: integer = 0; WriteVerToStream: Boolean = False); virtual;
property StreamVersion: integer read FStreamVersion;
end;
TStreamableList = class(TList)
private
FStreamVersion: integer;
public
constructor Create;
procedure LoadFromFile(FileName: WideString; Version: integer = 0; ReadVerFromStream: Boolean = False); virtual;
procedure LoadFromStream(S: TStream; Version: integer = 0; ReadVerFromStream: Boolean = False); virtual;
procedure SaveToFile(FileName: WideString; Version: integer = 0; ReadVerFromStream: Boolean = False); virtual;
procedure SaveToStream(S: TStream; Version: integer = 0; WriteVerToStream: Boolean = False); virtual;
property StreamVersion: integer read FStreamVersion;
end;
{-------------------------------------------------------------------------------}
{-------------------------------------------------------------------------------}
{ Our own COM like referenced classes }
{-------------------------------------------------------------------------------}
TReferenceCounted = class
protected
FRefCount: integer;
public
procedure AddRef;
procedure Release;
end;
{ Reference counted TList, much like a COM object but the compiler does not }
{ add the AddRef and Release call automaticlly. }
TReferenceCountedList = class(TList)
protected
FRefCount: integer;
public
procedure AddRef;
procedure Release;
property RefCount: integer read FRefCount;
end;
{-------------------------------------------------------------------------------}
{ Encapsulates IExtractImage, ASCI and Unicode }
{-------------------------------------------------------------------------------}
TExtractImage = class
private
FFlags: Longword; // Sets how the image is to be handled see IEIFLAG_xxxx
FPriority: Longword; // Returns from GetLocation call the priority if IEIFLAG_ASYNC is used above
FHeight: Longword; // Desired image height
FWidth: Longword; // Desired image Width
FColorDepth: Longword; // Desired color depth
FExtractImageInterface: IExtractImage; // The interface
FExtractImage2Interface: IExtractImage2; // The interface for image2
FOwner: TNamespace; // The Owner namespace
FPathExtracted: Boolean;
function GetImage: TBitmap;
function GetImagePath: WideString;
function GetExtractImageInterface: IExtractImage;
function GetExtractImageInterface2: IExtractImage2;
protected
property PathExtracted: Boolean read FPathExtracted write FPathExtracted;
public
constructor Create;
property ColorDepth: Longword read FColorDepth write FColorDepth;
property ImagePath: WideString read GetImagePath;
property Image: TBitmap read GetImage;
property ExtractImageInterface: IExtractImage read GetExtractImageInterface;
property ExtractImage2Interface: IExtractImage2 read GetExtractImageInterface2;
property Flags: Longword read FFlags write FFlags;
property Height: Longword read FHeight write FHeight;
property Owner: TNamespace read FOwner write FOwner;
property Priority: Longword read FPriority;
property Width: Longword read FWidth write FWidth;
end;
{-------------------------------------------------------------------------------}
{-------------------------------------------------------------------------------}
{ Encapsulates IShellLink, ASCI and Unicode }
{-------------------------------------------------------------------------------}
TVirtualShellLink = class(TComponent)
private
FFileName: WideString; // File name of the lnk file
FShellLinkA: IShellLink; // ShellLink interface
FShellLinkW: IShellLinkW; // ShellLinkW interface
FIconIndex: integer; // Index of the icon to be used with the link
FTargetIDList: PItemIDList; // If the Target is a virtual object the PIDL is the only way to make the link
FShowCmd: TCmdShow; // How to show the window of the target application
FHotKeyModifiers: THotKeyModifiers; // The key modifiers for short cuts
FTargetPath: WideString; // The target that will be executed
FArguments: WideString; // Any arguments to be passed to the target
FDescription: WideString; // A description that will be shown in the properties dialog
FWorkingDirectory: WideString; // The directory the target application will have set as its current directory
FIconLocation: WideString; // The file that has the icon for the link
FHotKey: Word; // The HotKey to execute the link, used with the FHotKeyModifiers
FSilentWrite: Boolean; // Do not check parameters before writing lnk file and show a warning
function GetShellLinkAInterface: IShellLinkA;
function GetShellLinkWInterface: IShellLinkW;
protected
procedure FreeTargetIDList;
public
destructor Destroy; override;
function ReadLink(LinkFileName: WideString): Boolean;
function WriteLink(LinkFileName: WideString): Boolean;
property Arguments: WideString read FArguments write FArguments;
property Description: WideString read FDescription write FDescription;
property FileName: WideString read FFileName write FFileName;
property HotKey: Word read FHotKey write FHotKey;
property HotKeyModifiers: THotKeyModifiers read FHotKeyModifiers write FHotKeyModifiers;
property IconIndex: integer read FIconIndex write FIconIndex;
property IconLocation: WideString read FIconLocation write FIconLocation;
property TargetIDList: PItemIDList read FTargetIDList write FTargetIDList;
property ShellLinkAInterface: IShellLink read GetShellLinkAInterface;
property ShellLinkWInterface: IShellLinkW read GetShellLinkWInterface;
property ShowCmd: TCmdShow read FShowCmd write FShowCmd; // SW_XXXX contants
property SilentWrite: Boolean read FSilentWrite write FSilentWrite;
property TargetPath: WideString read FTargetPath write FTargetPath;
property WorkingDirectory: WideString read FWorkingDirectory write FWorkingDirectory;
end;
{-------------------------------------------------------------------------------}
// General helper class to sort Shell related objects. Uses mainly to sort
// columns in details mode
TShellSortHelper = class
private
FFileSort: TFileSort; // Defines if SortType sorts by the type string or the file extension
public
function CompareIDSort(SortColumn: integer; NS1, NS2: TNamespace): Integer; virtual;
function DiscriminateFolders(NS1, NS2: TNamespace): Integer; virtual;
function SortFileSize(NS1, NS2: TNamespace): Integer; virtual;
function SortFileTime(FT1, FT2: TFileTime; NS1, NS2: TNamespace): Integer; virtual;
function SortString(S1, S2: WideString; NS1, NS2: TNamespace): Integer; virtual;
function SortType(NS1, NS2: TNamespace): Integer; virtual;
property FileSort: TFileSort read FFileSort write FFileSort;
end;
{-------------------------------------------------------------------------------}
{ Function definitions }
{-------------------------------------------------------------------------------}
// Return True if VT adds a node to the tree this keeps the item count returned
// by TNamespace.EnumFolder correct. To stop the enumeration set Terminate to true
TEnumFolderCallback = function(MessageWnd: HWnd; APIDL: PItemIDList; AParent: TNamespace;
Data: Pointer; var Terminate: Boolean): Boolean of object;
TContextMenuCmdCallback = procedure(Namespace: TNamespace; Verb: WideString;
MenuItemID: Integer; var Handled: Boolean) of object;
TContextMenuShowCallback = procedure(Namespace: TNamespace; Menu: hMenu;
var Allow: Boolean) of object;
TContextMenuAfterCmdCallback = procedure(Namespace: TNamespace; Verb: WideString;
MenuItemID: Integer; Successful: Boolean) of object;
{-------------------------------------------------------------------------------}
{-------------------------------------------------------------------------------}
{ TNamespace, encapsulates the Windows Shell Namespace }
{-------------------------------------------------------------------------------}
{ TNamespace is a class that encapsulates the IShellFolder interface. It }
{ simplifies shell interfaces by hiding the overhead of PIDLs and COM. }
{ Most properties and methods have a direct corrolation to the functions }
{ exposed by IShellFolder. }
TNamespace = class
private
FAbsolutePIDL: PItemIDList; // The Absolute PIDL of that represents the namespace
FCatInfo: TCategoriesInfo;
FCurrentContextMenu: IContextMenu; // The basic interface to create a shell context menu, need to save because of ownerdraw callbacks (maybe this one is not necessary IContextMenu2 only supports this)
FCurrentContextMenu2: IContextMenu2; // Extends the context menu interface to include ownerdraw items, need to save because of ownerdraw callbacks
FDropTargetInterface: IDropTarget; // Interface to handle a drag Drop on the namespace
FExtractImage: TExtractImage; // Encapsulate the seldom used IExtractImage inteface saving memory allocation in the TNamespace when not used
FImage: TBitmap; // The image extracted from the IExtractImage interface
FNamespaceID: integer; // ID of the namespace. Used to pick out any custom namespace objects from real shell supplied ones
FOldWndProcForContextMenu: TWndMethod; // OldWndProc of the ContextMenu owner used in InternalShowContextMenu
FParent: TNamespace; // The parent of this namespace, may be owned by this decenant see OwnsParent property
FRelativePIDL: PItemIDList; // The relative PILD that can be used the the ParentNamespace. It is a pointer to the last ID of of AbsolutePILD so *don't* free it
FShellDetailsInterface: IVETShellDetails; // Interface to deal with the information in the columns in details view (superceded by IShellFolder2 )
FShellFolder: IShellFolder; // IShellFolder is the building block interface that defines the namespaces attributes
FShellFolder2: IShellFolder2; // Expands IShellFolder handling the column details in Details mode, only works on Win2k-WinMe and up
FShellIconInterface: IShellIcon; // Interface to extract only the index of the icon in the system imagelist
FShellLink: TVirtualShellLink; // Object to read and write attributes to shortcut namespaces (files)
FSHGetFileInfoRec: PSHGetFileInfoRec; // Stores cached info from a call to SHGetFileInfo(A or W)
FStates: TNamespaceStates; // Dynamic state of the TNamespace
FTag: integer;
FTileDetail: TCommonIntegerDynArray; //
FQueryInfoInterface: IQueryInfo; // Interface for the popup InfoTips on folders in Win2k-WinME and up
FWin32FindDataA: PWin32FindDataA; // pointer to an allocated structure for an ASCI window file information if is is a file object
FWin32FindDataW: PWin32FindDataW; // pointer to an allocated structure for an Unicode window file information if is is a file object
FSystemIsSuperHidden: Boolean; // Holds the result of if the system has the SuperHiddenFile flag set in the registry
FShellIconOverlayInterface: IShellIconOverlay;
FCategoryProviderInterface: ICategoryProvider;
FBrowserFrameOptionsInterface: IBrowserFrameOptions;
FQueryAssociationsInterface: IQueryAssociations;
function GetCategoryCount: Integer;
function GetParent: TNamespace;
protected
{ Make the Cache Data and property getters available to decendants. This }
{ will allow decendants of TNamespace to be created so "virtual namespaces" }
{ can be created. It is possible to create a "namespace extension" without }
{ really doing it! }
{ None of interface properties are here because they only make sense for }
{ actual COM namespaces. }
FShellCache: TShellCacheRec;
{ Virtual Property Setters }
function GetArchive: Boolean; virtual;
function GetAttributesString: WideString; virtual;
function GetBrowsable: Boolean; virtual;
function GetBrowserFrameOptionsInterface: IBrowserFrameOptions; virtual;
function GetCanCopy: Boolean; virtual;
function GetCanDelete: Boolean; virtual;
function GetCanLink: Boolean; virtual;
function GetCanMoniker: Boolean; virtual;
function GetCanMove: Boolean; virtual;
function GetCanRename: Boolean; virtual;
function GetCategoryProviderInterface: ICategoryProvider; virtual;
function GetCLSID: TGUID; virtual;
function GetCompressed: Boolean; virtual;
function GetContextMenu2Interface: IContextMenu2; virtual;
function GetContextMenu3Interface: IContextMenu3; virtual;
function GetContextMenuInterface: IContextMenu; virtual;
function GetCreationDateTime: TDateTime; virtual;
function GetCreationTime: WideString; virtual;
function GetCreationTimeRaw: TFileTime; virtual;
function GetDataObjectInterface: IDataObject; virtual;
function GetDescription: TObjectDescription; virtual;
function GetDetailsSupported: Boolean; virtual;
function GetDirectory: Boolean; virtual;
function GetDropTarget: Boolean; virtual;
function GetDropTargetInterface: IDropTarget; virtual;
function GetEncrypted: Boolean; virtual;
function GetExtension: WideString; virtual;
function GetExtractIconAInterface: IExtractIconA; virtual;
function GetExtractIconWInterface: IExtractIconW; virtual;
function GetExtractImage: TExtractImage; virtual;
function GetFileName: WideString; virtual;
function GetFileSysAncestor: Boolean; virtual;
function GetFileSystem: Boolean; virtual;
function GetFileType: WideString; virtual;
function GetFolder: Boolean; virtual;
function GetFreePIDLOnDestroy: Boolean; virtual;
function GetGhosted: Boolean; virtual;
function GetHasPropSheet: Boolean; virtual;
function GetHasStorage: Boolean; virtual;
function GetHasSubFolder: Boolean; virtual;
function GetHidden: Boolean; virtual;
function GetIconIndexChanged: Boolean; virtual;
function GetInfoTip: WideString; virtual;
function GetIsSlow: Boolean; virtual;
function GetLastAccessDateTime: TDateTime; virtual;
function GetLastAccessTime: WideString; virtual;
function GetLastAccessTimeRaw: TFileTime; virtual;
function GetLastWriteDateTime: TDateTime; virtual;
function GetLastWriteTime: WideString; virtual;
function GetLastWriteTimeRaw: TFileTime; virtual;
function GetLink: Boolean; virtual;
function GetNameAddressbar: WideString; virtual;
function GetNameAddressbarInFolder: WideString; virtual;
function GetNameForEditing: WideString; virtual;
function GetNameForEditingInFolder: WideString; virtual;
function GetNameForParsing: WideString; virtual;
function GetNameForParsingInFolder: WideString; virtual;
function GetNameInFolder: WideString; virtual;
function GetNameNormal: WideString; virtual;
function GetNameParseAddress: WideString; virtual;
function GetNameParseAddressInFolder: WideString; virtual;
function GetNewContent: Boolean; virtual;
function GetNonEnumerated: Boolean; virtual;
function GetNormal: Boolean; virtual;
function GetOffLine: Boolean; virtual;
function GetOverlayIconIndex: Integer; virtual;
function GetOverlayIndex: Integer; virtual;
function GetParentShellDetailsInterface: IVETShellDetails; virtual;
function GetParentShellFolder: IShellFolder; virtual;
function GetParentShellFolder2: IShellFolder2; virtual;
function GetQueryAssociationsInterface: IQueryAssociations; virtual;
function GetQueryInfoInterface: IQueryInfo;virtual;
function GetReadOnly: Boolean; virtual;
function GetReadOnlyFile: Boolean; virtual;
function GetReparsePoint: Boolean; virtual;
function GetRemovable: Boolean; virtual;
function GetShare: Boolean; virtual;
function GetShellDetailsInterface: IVETShellDetails; virtual;
function GetShellFolder: IShellFolder; virtual;
function GetShellFolder2: IShellFolder2; virtual;
function GetShellIconInterface: IShellIcon; virtual;
function GetShellIconOverlayInterface: IShellIconOverlay; virtual;
function GetShellLink: TVirtualShellLink; virtual;
function GetShortFileName: WideString; virtual;
function GetSizeOfFile: WideString; virtual;
function GetSizeOfFileDiskUsage: WideString; virtual;
function GetSizeOfFileInt64: Int64; virtual;
function GetSizeOfFileKB: WideString; virtual;
function GetSparseFile: Boolean; virtual;
function GetStorage: Boolean; virtual;
function GetStorageAncestor: Boolean; virtual;
function GetStream: Boolean; virtual;
function GetSubFolders: Boolean; virtual;
function GetSubItems: Boolean; virtual;
function GetSystem: Boolean; virtual;
function GetTemporary: Boolean; virtual;
function GetThreadedDetailLoaded(ColumnIndex: Integer): Boolean; virtual;
function GetThreadedDetailLoading(ColumnIndex: Integer): Boolean; virtual;
function GetThreadedIconLoaded: Boolean; virtual;
function GetThreadedImageLoaded: Boolean; virtual;
function GetThreadedImageLoading: Boolean; virtual;
function GetThreadIconLoading: Boolean; virtual;
function GetValid: Boolean; virtual;
function ParentWnd: HWnd;
procedure EnsureDetailCache;
procedure ExecuteContextMenuVerbMultiPath(Verb: WideString; Namespaces: TNamespaceArray);
procedure LoadCategoryInfo;
procedure SetFreePIDLOnDestroy(const Value: Boolean); virtual;
procedure SetIconIndexChanged(const Value: Boolean); virtual;
procedure SetThreadedDetailLoaded(ColumnIndex: Integer; Value: Boolean); virtual;
procedure SetThreadedDetailLoading(ColumnIndex: Integer; Value: Boolean); virtual;
procedure SetThreadIconLoading(const Value: Boolean); virtual;
procedure SetThreadImageLoading(const Value: Boolean); virtual;
function CreateCategory(GUID: TGUID): ICategorizer;
function EnumFuncDummy(MessageWnd: HWnd; APIDL: PItemIDList; AParent: TNamespace; Data: Pointer; var Terminate: Boolean): Boolean;
function ExplorerStyleAttributeStringList(CapitalLetters: Boolean): WideString;
function DisplayNameOf(Flags: Longword): WideString;
procedure GetDataFromIDList;
procedure GetFileTimes;
procedure GetSHFileInfo;
function InjectCustomSubMenu(Menu: HMenu; Caption: string; PopupMenu: TPopupMenu; var SubMenu: HMenu): TMenuItemIDArray;
function InternalGetContextMenuInterface(PIDLArray: TRelativePIDLArray): IContextMenu;
function InternalGetDataObjectInterface(PIDLArray: TRelativePIDLArray): IDataObject;
function InternalShowContextMenu(Owner: TWinControl; ContextMenuCmdCallback: TContextMenuCmdCallback;
ContextMenuShowCallback: TContextMenuShowCallback; ContextMenuAfterCmdCallback: TContextMenuAfterCmdCallback;
PIDLArray: TRelativePIDLArray; Position: PPoint;
CustomShellSubMenu: TPopupMenu; CustomSubMenuCaption: WideString): Boolean;
function InternalSubItems(Flags: Longword): Boolean;
procedure ReplacePIDL(NewPIDL: PItemIDList; AParent: TNamespace);
function ShowContextMenuMultiPath(Owner: TWinControl; Focused: TNamespace;
Namespaces: TNamespaceArray; Position: PPoint = nil): Boolean;
procedure WindowProcForContextMenu(var Message: TMessage);
property CatInfo: TCategoriesInfo read FCatInfo write FCatInfo;
property CurrentContextMenu2: IContextMenu2 read FCurrentContextMenu2 write FCurrentContextMenu2;
property CurrentContextMenu: IContextMenu read FCurrentContextMenu write FCurrentContextMenu;
property ShellCache: TShellCacheRec read FShellCache write FShellCache;
property SystemIsSuperHidden: Boolean read FSystemIsSuperHidden write FSystemIsSuperHidden;
public
constructor Create(PIDL: PItemIdList; AParent: TNamespace);
destructor Destroy; override;
constructor CreateCustomNamespace(CustomID: Integer; AParent: TNamespace); virtual;
constructor CreateFromFileName(FileName: WideString); virtual;
function CanCopyAll(NamespaceArray: TNamespaceArray): Boolean; virtual;
function CanCutAll(NamespaceArray: TNamespaceArray): boolean; virtual;
function CanDeleteAll(NamespaceArray: TNamespaceArray): Boolean; virtual;
function CanPasteToAll(NamespaceArray: TNamespaceArray): Boolean; virtual;
function CanShowPropertiesOfAll(NamespaceArray: TNamespaceArray): Boolean; virtual;
function Clone(ReleasePIDLOnDestroy: Boolean): TNameSpace; virtual;
function ComparePIDL(PIDLToCompare: PItemIDList; IsAbsolutePIDL: Boolean; Column: Integer = 0): ShortInt; virtual;
function ContextMenuItemHelp(MenuItemID: LongWord): WideString; virtual;
function ContextMenuVerb(MenuItemID: LongWord): WideString; virtual;
function Copy(NamespaceArray: TNamespaceArray): Boolean; virtual;
function Cut(NamespaceArray: TNamespaceArray): Boolean; virtual;
function DataObjectMulti(NamespaceArray: TNamespaceArray): IDataObject; virtual;
function Delete(NamespaceArray: TNamespaceArray): Boolean; virtual;
function DetailsAlignment(ColumnIndex: Integer): TAlignment; virtual;
function DetailsColumnTitle(ColumnIndex: integer): WideString; virtual;
function DetailsDefaultColumnTitle(ColumnIndex: integer): WideString; virtual;
function DetailsDefaultOf(ColumnIndex: integer): WideString; virtual;
function DetailsDefaultSupportedColumns: integer; virtual;
function DetailsGetDefaultColumnState(ColumnIndex: integer): TSHColumnStates; virtual;
function DetailsOf(ColumnIndex: integer): WideString; virtual;
function DetailsOfEx(ColumnIndex: integer): WideString; virtual;
function DetailsSupportedColumns: integer; virtual;
function DetailsSupportedVisibleColumns: TVisibleColumnIndexArray; virtual;
function DetailsValidIndex(DetailsIndex: integer): Boolean; virtual;
function DragEffect(grfKeyState: integer): HRESULT; virtual;
function DragEnter(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult; virtual;
function DragLeave: HResult; virtual;
function DragOver(grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult; virtual;
function Drop(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult; virtual;
function EnumerateFolder(MessageWnd: HWnd; Folders, NonFolders, IncludeHidden: Boolean;
EnumFunc: TEnumFolderCallback; UserData: pointer): integer; virtual;
function EnumerateFolderEx(MessageWnd: HWnd; FileObjects: TFileObjects; EnumFunc: TEnumFolderCallback; UserData: pointer; AfterValidEnumIDList: TNotifyEvent = nil): integer; virtual;
function ExecuteContextMenuVerb(AVerb: WideString; APIDLArray: TRelativePIDLArray; MessageWindowParent: HWnd = 0): Boolean; virtual;
function FolderSize(Invalidate: Boolean; RecurseFolder: Boolean = False): Int64; virtual;
function GetIconIndex(OpenIcon: Boolean; IconSize: TIconSize; ForceLoad: Boolean = True): integer; virtual;
function GetImage: TBitmap; virtual;
function VerifyPIDLRelationship(NamespaceArray: TNamespaceArray;
Silent: Boolean = False): Boolean;
procedure HandleContextMenuMsg(Msg, wParam, lParam: Longint; var Result: LRESULT); virtual;
procedure InvalidateCache; virtual;
procedure InvalidateDetailsOfCache(FlushStrings: Boolean);
procedure InvalidateNamespace(RefreshIcon: Boolean = True); virtual;
procedure InvalidateRelativePIDL(FileObjects: TFileObjects); virtual;
procedure InvalidateThumbImage; virtual;
function IsChildByNamespace(TestNamespace: TNamespace; Immediate: Boolean): Boolean; virtual;
function IsChildByPIDL(TestPIDL: PItemIDList; Immediate: Boolean): Boolean; virtual;
function IsChildOfRemovableDrive: Boolean; virtual;
function IsControlPanel: Boolean; virtual;
function IsControlPanelChildFolder: Boolean; virtual;
function IsDesktop: Boolean; virtual;
function IsMyComputer: Boolean; virtual;
function IsNetworkNeighborhood: Boolean; virtual;
function IsNetworkNeighborhoodChild: Boolean; virtual;
function IsParentByNamespace(TestNamespace: TNamespace; Immediate: Boolean): Boolean; virtual;
function IsParentByPIDL(TestPIDL: PItemIDList; Immediate: Boolean): Boolean; virtual;
function IsRecycleBin: Boolean;
function OkToBrowse(ShowExplorerMsg: Boolean): Boolean; virtual;
function ParseDisplayName: PItemIDList; overload; virtual;
function ParseDisplayName(Path: WideString): PItemIDList; overload; virtual;
function Paste(NamespaceArray: TNamespaceArray; AsShortCut: Boolean = False): Boolean; virtual;
procedure SetDetailByThread(ColumnIndex: Integer; Detail: WideString);
procedure SetIconIndexByThread(IconIndex: Integer; ClearThreadLoading: Boolean); virtual;
procedure SetImageByThread(Bitmap: TBitmap; ClearThreadLoading: Boolean); virtual;
function SetNameOf(NewName: WideString): Boolean; virtual;
function ShellExecuteNamespace(WorkingDir, CmdLineArguments: WideString; ExecuteFolder: Boolean = False;
ExecuteFolderShortCut: Boolean = False; RunInThread: Boolean = False): Boolean; virtual;
function ShowContextMenu(Owner: TWinControl;
ContextMenuCmdCallback: TContextMenuCmdCallback; ContextMenuShowCallback: TContextMenuShowCallback;
ContextMenuAfterCmdCallback: TContextMenuAfterCmdCallback; Position: PPoint = nil;
CustomShellSubMenu: TPopupMenu = nil; CustomSubMenuCaption: WideString = ''): Boolean; virtual;
function ShowContextMenuMulti(Owner: TWinControl;
ContextMenuCmdCallback: TContextMenuCmdCallback;
ContextMenuShowCallback: TContextMenuShowCallback;
ContextMenuAfterCmdCallback: TContextMenuAfterCmdCallback;
NamespaceArray: TNamespaceArray; Position: PPoint = nil;
CustomShellSubMenu: TPopupMenu = nil;
CustomSubMenuCaption: WideString = ''): Boolean; virtual;
procedure ShowPropertySheet; virtual;
procedure ShowPropertySheetMulti(NamespaceArray: TNamespaceArray;
UseSHMultiFileProperties: Boolean = True;
ForceNonMultiPath: Boolean = False); virtual;
function SubFoldersEx(Flags: Longword = SHCONTF_FOLDERS): Boolean; virtual;
function SubItemsEx(Flags: Longword = SHCONTF_NONFOLDERS): Boolean; virtual;
function TestAttributesOf(Flags: Longword; FlushCache: Boolean; SoftFlush: Boolean = False): Boolean; virtual;
property AbsolutePIDL: PItemIDList read FAbsolutePIDL write FAbsolutePIDL;
property AdvDetailsSupported: Boolean read GetDetailsSupported;
property Browsable: Boolean read GetBrowsable;
property BrowserFrameOptionsInterface: IBrowserFrameOptions read GetBrowserFrameOptionsInterface;
property CanCopy: Boolean read GetCanCopy;
property CanDelete: Boolean read GetCanDelete;
property CanLink: Boolean read GetCanLink;
property CanMoniker: Boolean read GetCanMoniker;
property CanMove: Boolean read GetCanMove;
property CanRename: Boolean read GetCanRename;
property CategoryCount: Integer read GetCategoryCount;
property CategoryProviderInterface: ICategoryProvider read GetCategoryProviderInterface;
property CLSID: TGUID read GetCLSID;
property ContextMenuInterface: IContextMenu read GetContextMenuInterface;
property ContextMenu2Interface: IContextMenu2 read GetContextMenu2Interface;
property ContextMenu3Interface: IContextMenu3 read GetContextMenu3Interface;
property DataObjectInterface: IDataObject read GetDataObjectInterface;
property Description: TObjectDescription read GetDescription;
property DropTarget: Boolean read GetDropTarget;
property DropTargetInterface: IDropTarget read GetDropTargetInterface;
property Encrypted: Boolean read GetEncrypted;
property ExtractImage: TExtractImage read GetExtractImage;
property ExtractIconAInterface: IExtractIconA read GetExtractIconAInterface;
property ExtractIconWInterface: IExtractIconW read GetExtractIconWInterface;
property FileSystem: Boolean read GetFileSystem;
property FileSysAncestor: Boolean read GetFileSysAncestor;
property Folder: Boolean read GetFolder;
property FreePIDLOnDestroy: Boolean read GetFreePIDLOnDestroy write SetFreePIDLOnDestroy;
property Ghosted: Boolean read GetGhosted;
property HasPropSheet: Boolean read GetHasPropSheet;
property HasStorage: Boolean read GetHasStorage;
property HasSubFolder: Boolean read GetHasSubFolder;
property IconIndexChanged: Boolean read GetIconIndexChanged write SetIconIndexChanged;
property IsSlow: Boolean read GetIsSlow;
property Link: Boolean read GetLink;
property InfoTip: WideString read GetInfoTip;
property NameAddressbar: WideString read GetNameAddressbar;
property NameAddressbarInFolder: WideString read GetNameAddressbarInFolder;
property NameForEditing: WideString read GetNameForEditing;
property NameForEditingInFolder: WideString read GetNameForEditingInFolder;
property NameForParsing: WideString read GetNameForParsing;
property NameForParsingInFolder: WideString read GetNameForParsingInFolder;
property NameInFolder: WideString read GetNameInFolder;
property NameNormal: WideString read GetNameNormal;
property NameParseAddress: WideString read GetNameParseAddress;
property NameParseAddressInFolder: WideString read GetNameParseAddressInFolder;
property NamespaceID: integer read FNamespaceID;
property NewContent: Boolean read GetNewContent;
property NonEnumerated: Boolean read GetNonEnumerated;
property Parent: TNamespace read GetParent;
property ParentShellFolder: IShellFolder read GetParentShellFolder;
property ParentShellFolder2: IShellFolder2 read GetParentShellFolder2;
property ParentShellDetailsInterface: IVETShellDetails read GetParentShellDetailsInterface;
property QueryAssociationsInterface: IQueryAssociations read GetQueryAssociationsInterface;
property ReadOnly: Boolean read GetReadOnly;
property RelativePIDL: PItemIDList read FRelativePIDL; // Single Item ID of this namespace
property Removable: Boolean read GetRemovable;
property Share: Boolean read GetShare;
property ShellFolder: IShellFolder read GetShellFolder;
property ShellFolder2: IShellFolder2 read GetShellFolder2;
property ShellDetailsInterface: IVETShellDetails read GetShellDetailsInterface;
property ShellLink: TVirtualShellLink read GetShellLink;
property ShellIconInterface: IShellIcon read GetShellIconInterface;
property ShellIconOverlayInterface: IShellIconOverlay read GetShellIconOverlayInterface;
property ShortFileName: WideString read GetShortFileName;
property SparseFile: Boolean read GetSparseFile;
property States: TNamespaceStates read FStates write FStates;
property Storage: Boolean read GetStorage;
property StorageAncestor: Boolean read GetStorageAncestor;
property Stream: Boolean read GetStream;
property SubFolders: Boolean read GetSubFolders;
property SubItems: Boolean read GetSubItems;
property Tag: integer read FTag write FTag;
property ThreadedDetailLoaded[Column: Integer]: Boolean read GetThreadedDetailLoaded write SetThreadedDetailLoaded;
property ThreadedDetailLoading[Column: Integer]: Boolean read GetThreadedDetailLoading write SetThreadedDetailLoading;
property ThreadedIconLoaded: Boolean read GetThreadedIconLoaded;
property ThreadIconLoading: Boolean read GetThreadIconLoading write SetThreadIconLoading;
property ThreadImageLoaded: Boolean read GetThreadedImageLoaded;
property ThreadImageLoading: Boolean read GetThreadedImageLoading write SetThreadImageLoading;
property TileDetail: TCommonIntegerDynArray read FTileDetail write FTileDetail;
property QueryInfoInterface: IQueryInfo read GetQueryInfoInterface;
property Win32FindDataA: PWin32FindDataA read FWin32FindDataA;
property Win32FindDataW: PWin32FindDataW read FWin32FindDataW;
{ Information on namespaces that are actual files. }
property AttributesString: WideString read GetAttributesString; // Explorer type 'RHSA'
property Archive: Boolean read GetArchive;
property Compressed: Boolean read GetCompressed;
property CreationTime: WideString read GetCreationTime;
property CreationDateTime: TDateTime read GetCreationDateTime;
property CreationTimeRaw: TFileTime read GetCreationTimeRaw;
property Directory: Boolean read GetDirectory;
property Extension: WideString read GetExtension;
property FileName: WideString read GetFileName;
property FileType: WideString read GetFileType;
property Hidden: Boolean read GetHidden;
property LastAccessTime: WideString read GetLastAccessTime;
property LastAccessDateTime: TDateTime read GetLastAccessDateTime;
property LastAccessTimeRaw: TFileTime read GetLastAccessTimeRaw;
property LastWriteTime: WideString read GetLastWriteTime;
property LastWriteDateTime: TDateTime read GetLastWriteDateTime;
property LastWriteTimeRaw: TFileTime read GetLastWriteTimeRaw;
property Normal: Boolean read GetNormal;
property OffLine: Boolean read GetOffLine;
property OverlayIndex: Integer read GetOverlayIndex;
property OverlayIconIndex: Integer read GetOverlayIconIndex;
property ReadOnlyFile: Boolean read GetReadOnlyFile;
property ReparsePoint: Boolean read GetReparsePoint;
property SizeOfFile: WideString read GetSizeOfFile;
property SizeOfFileInt64: Int64 read GetSizeOfFileInt64;
property SizeOfFileKB: WideString read GetSizeOfFileKB;
property SizeOfFileDiskUsage: WideString read GetSizeOfFileDiskUsage;
property SystemFile: Boolean read GetSystem;
property Temporary: Boolean read GetTemporary;
property Valid: Boolean read GetValid;
end;
{-------------------------------------------------------------------------------}
{$IFNDEF COMPILER_5_UP}
TObjectList = class(TList)
private
FOwnsObjects: Boolean;
protected
function GetItem(Index: Integer): TObject;
procedure SetItem(Index: Integer; AObject: TObject);
public
constructor Create; overload;
constructor Create(AOwnsObjects: Boolean); overload;
function Add(AObject: TObject): Integer;
function Remove(AObject: TObject): Integer;
function IndexOf(AObject: TObject): Integer;
function FindInstanceOf(AClass: TClass; AExact: Boolean = True; AStartAt: Integer = 0): Integer;
procedure Insert(Index: Integer; AObject: TObject);
function First: TObject;
function Last: TObject;
property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
property Items[Index: Integer]: TObject read GetItem write SetItem; default;
end;
{$ENDIF}
TVirtualNameSpaceList = class(TObjectList)
private
function GetItems(Index: Integer): TNamespace;
procedure SetItems(Index: Integer; ANamespace: TNamespace);
public
function Add(ANamespace: TNamespace): Integer;
procedure FillArray(var NamespaceArray: TNamespaceArray);
function IndexOf(ANamespace: TNamespace): Integer;
procedure Insert(Index: Integer; ANamespace: TNamespace);
property Items[Index: Integer]: TNamespace read GetItems write SetItems; default;
end;
//
// Used to operate on files that are not the immediate children of the parent
// for instance in a search list
//
// This class will only work on a system that has Shell32.dll version 4.71 and
// higher. If the system has at least IE 4.0 it should be compatible
//
// Use this class as the starting point for a custom Shell Context Menu. It
// will handle all of the localization of names for you.
// It will also handle objects that do not have the same immediate parent such
// as in a search list.
//
// There is some danger to this as the app could free the object while
// the shell still has a connection to it through IShellFolder or IDropTarget.
// It should be virtually impossible to do this as the shell only connects to
// the interfaces during the time the menu is open.
PMenuItemLink = ^TMenuItemLink;
TMenuItemLink = record
MenuID: UINT;
{$IFDEF TNTSUPPORT}
Item: TTntMenuItem;
{$ELSE}
Item: TMenuItem
{$ENDIF}
end;
TMenuItemMap = class(TList)
protected
function Get(Index: Integer): PMenuItemLink;
procedure Put(Index: Integer; Item: PMenuItemLink);
public
function Add: PMenuItemLink;
function First: PMenuItemLink;
function IndexOf(Item: PMenuItemLink): Integer;
procedure Clear; override;
function Insert(Index: Integer): PMenuItemLink; reintroduce;
function Last: PMenuItemLink;
function Remove(Item: PMenuItemLink): Integer;
property Items[Index: Integer]: PMenuItemLink read Get write Put; default;
end;
TCommonShellContextMenu = class(TComponent, IUnknown, IShellFolder, IDropTarget)
private
FActiveFolder: IShellFolder;
FActivePIDLs: TPIDLArray;
FCopyValidated: Boolean;
FCurrentContextMenu: IContextMenu;
FCurrentContextMenu2: IContextMenu2;
FCutValidated: Boolean;
FExtensions: TCommonShellContextMenuExtensions;
FFromDesktop: Boolean;
FLocalFocused: TNamespace;
FLocalNamespaces: TNamespaceArray;
FMenuMap: TMenuItemMap;
FOldWndProcForContextMenu: TWndMethod;
{$IFDEF TNTSUPPORT}
FKeyStrings: TTntStringList;
{$ELSE}
FKeyStrings: TStringList;
{$ENDIF}
FOnHide: TCommonShellMenuEvent;
FOnInvokeCommand: TCommonShellMenuInvokeEvent;
FOnMenuMerge: TCommonShellMenuMergeEvent;
FOnMenuMergeBottom: TCommonShellMenuMergeEvent;
FOnMenuMergeTop: TCommonShellMenuMergeEvent;
FOnShellMenuCopy: TCommonShellMenuCopyEvent;
FOnShellMenuCreateShortcut: TCommonShellMenuCreateShortcutEvent;
FOnShellMenuCut: TCommonShellMenuCutEvent;
FOnShellMenuDelete: TCommonShellMenuDeleteEvent;
FOnShellMenuNewFolder: TCommonShellMenuNewFolderEvent;
FOnShellMenuPaste: TCommonShellMenuPasteEvent;
FOnShellMenuPasteLink: TCommonShellMenuPasteLinkEvent;
FOnShellMenuPasteShortCut: TCommonShellMenuPasteShortCutEvent;
FOnShellMenuProperites: TCommonShellMenuProperitesEvent;
FOnShow: TCommonShellMenuEvent;
FRenameMenuItem: Boolean;
FStub: Pointer;
FPasteMenuItem: Boolean;
FUIObjectOfDataObject: IDataObject;
FUIObjectOfDropTarget: IDropTarget;
protected
// IUnknown
function QueryInterface(const IID: TGUID; out Obj): HResult; override; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
// IShellFolder
function ParseDisplayName(hwndOwner: HWND; pbcReserved: Pointer; lpszDisplayName: POLESTR; out pchEaten: ULONG; out ppidl: PItemIDList; var dwAttributes: ULONG): HResult; stdcall;
function EnumObjects(hwndOwner: HWND; grfFlags: DWORD; out EnumIDList: IEnumIDList): HResult; stdcall;
function BindToObject(pidl: PItemIDList; pbcReserved: Pointer; const riid: TIID; out ppvOut{$IFNDEF COMPILER_5_UP}: Pointer{$ENDIF}): HResult; stdcall;
function BindToStorage(pidl: PItemIDList; pbcReserved: Pointer; const riid: TIID; out ppvObj{$IFNDEF COMPILER_5_UP}: Pointer{$ENDIF}): HResult; stdcall;
function CompareIDs(lParam: LPARAM; pidl1, pidl2: PItemIDList): HResult; stdcall;
function CreateViewObject(hwndOwner: HWND; const riid: TIID; out ppvOut{$IFNDEF COMPILER_5_UP}: Pointer{$ENDIF}): HResult; stdcall;
function GetAttributesOf(cidl: UINT; var apidl: PItemIDList; var rgfInOut: UINT): HResult; stdcall;
function GetUIObjectOf(hwndOwner: HWND; cidl: UINT; var apidl: PItemIDList; const riid: TIID; prgfInOut: Pointer; out ppvOut{$IFNDEF COMPILER_5_UP}: Pointer{$ENDIF}): HResult; stdcall;
function GetDisplayNameOf(pidl: PItemIDList; uFlags: DWORD; var lpName: TStrRet): HResult; stdcall;
function SetNameOf(hwndOwner: HWND; pidl: PItemIDList; lpszName: POLEStr; uFlags: DWORD; var ppidlOut: PItemIDList): HResult; stdcall;
// IDropTarget
function DragEnter(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
function DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
function DragLeave: HResult; stdcall;
function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
function DefMenuCreateCallback(const psf: IShellfolder; wnd: HWND; const pdtObj: IDataObject; uMsg: UINT; WParm: WParam; lParm: LParam): HResult; stdcall;
procedure AddMenuKey(Key: WideString);
{$IFDEF TNTSUPPORT}
procedure AddMenuKeys(Keys: TTntStringList);
{$ELSE}
procedure AddMenuKeys(Keys: TStringList);
{$ENDIF}
procedure ClearKeys;
procedure DoCopy(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean); virtual;
procedure DoCreateShortCut(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean); virtual;
procedure DoCut(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean); virtual;
procedure DoDelete(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean); virtual;
procedure DoHide; virtual;
{$IFDEF TNTSUPPORT}
procedure DoInvokeCommand(MenuItem: TTntMenuItem; InvokeInfo: PCMInvokeCommandInfo); virtual;
{$ELSE}
procedure DoInvokeCommand(MenuItem: TMenuItem; InvokeInfo: PCMInvokeCommandInfo); virtual;
{$ENDIF}
procedure DoMenuMerge(Menu: HMENU; IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags); virtual;
procedure DoMenuMergeBottom(Menu: HMENU; IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags); virtual;
procedure DoMenuMergeTop(Menu: HMENU; IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags); virtual;
procedure DoNewFolder(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean); virtual;
procedure DoPaste(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean); virtual;
procedure DoPasteLink(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean); virtual;
procedure DoPasteShortCut(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean); virtual;
procedure DoProperties(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean); virtual;
procedure DoShow; virtual;
function DuplicateKey(Key: HKEY): HKEY;
{$IFDEF TNTSUPPORT}
function FindCommandId(CmdID: UINT; var MenuItem: TTntMenuItem): Boolean;
{$ELSE}
function FindCommandId(CmdID: UINT; var MenuItem: TMenuItem): Boolean;
{$ENDIF}
procedure HandleContextMenuMsg(Msg, wParam, lParam: Longint; var Result: LRESULT); stdcall;
function InternalShowContextMenu(Owner: TWinControl; ParentPIDL: PItemIDList; ChildPIDLs: TAbsolutePIDLArray; Verb: WideString; Position: PPoint = nil): Boolean;
procedure LoadMultiFolderPIDLArray(Namespaces: TNamespaceArray; var PIDLs: TAbsolutePIDLArray);
procedure LoadRegistryKeyStrings(Focused: TNamespace); virtual; abstract;
procedure WindowProcForContextMenu(var Message: TMessage);
property ActiveFolder: IShellFolder read FActiveFolder write FActiveFolder;
property ActivePIDLs: TPIDLArray read FActivePIDLs write FActivePIDLs;
property CopyValidated: Boolean read FCopyValidated write FCopyValidated;
property CurrentContextMenu2: IContextMenu2 read FCurrentContextMenu2 write FCurrentContextMenu2;
property CurrentContextMenu: IContextMenu read FCurrentContextMenu write FCurrentContextMenu;
property CutValidated: Boolean read FCutValidated write FCutValidated;
property Extensions: TCommonShellContextMenuExtensions read FExtensions write FExtensions;
property FromDesktop: Boolean read FFromDesktop write FFromDesktop;
{$IFDEF TNTSUPPORT}
property KeyStrings: TTntStringList read FKeyStrings write FKeyStrings;
{$ELSE}
property KeyStrings: TStringList read FKeyStrings write FKeyStrings;
{$ENDIF}
property LocalFocused: TNamespace read FLocalFocused write FLocalFocused;
property LocalNamespaces: TNamespaceArray read FLocalNamespaces write FLocalNamespaces;
property MenuMap: TMenuItemMap read FMenuMap write FMenuMap;
property OnHide: TCommonShellMenuEvent read FOnHide write FOnHide;
property OnInvokeCommand: TCommonShellMenuInvokeEvent read FOnInvokeCommand write FOnInvokeCommand;
property OnMenuMerge: TCommonShellMenuMergeEvent read FOnMenuMerge write FOnMenuMerge;
property OnMenuMergeBottom: TCommonShellMenuMergeEvent read FOnMenuMergeBottom write FOnMenuMergeBottom;
property OnMenuMergeTop: TCommonShellMenuMergeEvent read FOnMenuMergeTop write FOnMenuMergeTop;
property OnShellMenuCopy: TCommonShellMenuCopyEvent read FOnShellMenuCopy write FOnShellMenuCopy;
property OnShellMenuCreateShortCut: TCommonShellMenuCreateShortcutEvent read FOnShellMenuCreateShortcut write FOnShellMenuCreateShortcut;
property OnShellMenuCut: TCommonShellMenuCutEvent read FOnShellMenuCut write FOnShellMenuCut;
property OnShellMenuDelete: TCommonShellMenuDeleteEvent read FOnShellMenuDelete write FOnShellMenuDelete;
property OnShellMenuNewFolder: TCommonShellMenuNewFolderEvent read FOnShellMenuNewFolder write FOnShellMenuNewFolder;
property OnShellMenuPaste: TCommonShellMenuPasteEvent read FOnShellMenuPaste write FOnShellMenuPaste;
property OnShellMenuPasteLink: TCommonShellMenuPasteLinkEvent read FOnShellMenuPasteLink write FOnShellMenuPasteLink;
property OnShellMenuPasteShortCut: TCommonShellMenuPasteShortCutEvent read FOnShellMenuPasteShortCut write FOnShellMenuPasteShortCut;
property OnShellMenuProperites: TCommonShellMenuProperitesEvent read FOnShellMenuProperites write FOnShellMenuProperites;
property OnShow: TCommonShellMenuEvent read FOnShow write FOnShow;
property Stub: Pointer read FStub write FStub;
property RenameMenuItem: Boolean read FRenameMenuItem write FRenameMenuItem default True;
property PasteMenuItem: Boolean read FPasteMenuItem write FPasteMenuItem default True;
property UIObjectOfDataObject: IDataObject read FUIObjectOfDataObject write FUIObjectOfDataObject;
property UIObjectOfDropTarget: IDropTarget read FUIObjectOfDropTarget write FUIObjectOfDropTarget;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
{$IFDEF TNTSUPPORT}
function MergeMenuIntoContextMenu(Menu: TTntPopupMenu; ContextMenu: HMenu; Index: Integer; idStart: UINT): Integer;
{$ELSE}
function MergeMenuIntoContextMenu(Menu: TPopupMenu; ContextMenu: HMenu; Index: Integer; idStart: UINT): Integer;
{$ENDIF}
procedure ClearMenuMap;
end;
{*******************************************************************************}
{ Shell ContextMenu }
{*******************************************************************************}
TCommonShellBackgroundContextMenu = class(TCommonShellContextMenu)
private
FAutoDetectNewItem: Boolean;
FFinalItemList: TCommonPIDLList;
FInitialItemList: TCommonPIDLList;
FOnNewItem: TCommonShellMenuNewItemEvent;
{$IFDEF TNTSUPPORT}
FPaste: TTntMenuItem;
FPasteShortCut: TTntMenuItem;
FPopupMenuProperties: TTntPopupMenu;
FPopupMenuPaste: TTntPopupMenu;
FPopupMenuPasteShortCut: TTntPopupMenu;
FProperties: TTntMenuItem;
{$ELSE}
FPaste: TMenuItem;
FPasteShortCut: TMenuItem;
FPopupMenuProperties: TPopupMenu;
FPopupMenuPaste: TPopupMenu;
FPopupMenuPasteShortCut: TPopupMenu;
FProperties: TMenuItem;
{$ENDIF}
FShowPasteItem: Boolean;
FShowPasteShortCutItem: Boolean;
FShowPropertiesItem: Boolean;
protected
procedure ClickPaste(Sender: TObject);
procedure ClickPasteShortCut(Sender: TObject);
procedure ClickProperties(Sender: TObject);
procedure DoHide; override;
procedure DoMenuMerge(Menu: HMENU; IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags); override;
procedure DoMenuMergeBottom(Menu: HMENU; IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags); override;
procedure DoMenuMergeTop(Menu: HMENU; IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags); override;
procedure DoNewItem(NS: TNamespace); virtual;
procedure DoShow; override;
procedure LoadRegistryKeyStrings(Focused: TNamespace); override;
property FinalItemList: TCommonPIDLList read FFinalItemList write FFinalItemList;
property InitialItemList: TCommonPIDLList read FInitialItemList write FInitialItemList;
{$IFDEF TNTSUPPORT}
property Paste: TTntMenuItem read FPaste write FPaste;
property PasteShortCut: TTntMenuItem read FPasteShortCut write FPasteShortCut;
property PopupMenuProperties: TTntPopupMenu read FPopupMenuProperties write FPopupMenuProperties;
property PopupMenuPaste: TTntPopupMenu read FPopupMenuPaste write FPopupMenuPaste;
property PopupMenuPasteShortCut: TTntPopupMenu read FPopupMenuPasteShortCut write FPopupMenuPasteShortCut;
property Properties: TTntMenuItem read FProperties write FProperties;
{$ELSE}
property Paste: TMenuItem read FPaste write FPaste;
property PasteShortCut: TMenuItem read FPasteShortCut write FPasteShortCut;
property PopupMenuProperties: TPopupMenu read FPopupMenuProperties write FPopupMenuProperties;
property PopupMenuPaste: TPopupMenu read FPopupMenuPaste write FPopupMenuPaste;
property PopupMenuPasteShortCut: TPopupMenu read FPopupMenuPasteShortCut write FPopupMenuPasteShortCut;
property Properties: TMenuItem read FProperties write FProperties;
{$ENDIF}
property RenameMenuItem default False;
property PasteMenuItem default False;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function EnumCallback(MessageWnd: HWnd; APIDL: PItemIDList; AParent: TNamespace; Data: Pointer; var Terminate: Boolean): Boolean;
function ShowContextMenu(Owner: TWinControl; Root: TNamespace; Position: PPoint = nil): Boolean; virtual;
published
property AutoDetectNewItem: Boolean read FAutoDetectNewItem write FAutoDetectNewItem;
property OnHide;
property OnInvokeCommand;
property OnMenuMerge;
property OnMenuMergeBottom;
property OnMenuMergeTop;
property OnNewItem: TCommonShellMenuNewItemEvent read FOnNewItem write FOnNewItem;
property OnShow;
property ShowPasteItem: Boolean read FShowPasteItem write FShowPasteItem default True;
property ShowPasteShortCutItem: Boolean read FShowPasteShortCutItem write FShowPasteShortCutItem default True;
property ShowPropertiesItem: Boolean read FShowPropertiesItem write FShowPropertiesItem default True;
end;
TCommonShellMultiParentContextMenu = class(TCommonShellContextMenu)
protected
procedure LoadRegistryKeyStrings(Focused: TNamespace); override;
public
function ExecuteContextMenuVerb(Owner: TWinControl; Namespaces: TNamespaceArray; Verb: string): Boolean; virtual;
function ShowContextMenu(Owner: TWinControl; Focused: TNamespace; Namespaces: TNamespaceArray; Position: PPoint = nil): Boolean; virtual;
published
property OnHide;
property OnShellMenuCopy;
property OnShellMenuCreateShortCut;
property OnShellMenuCut;
property OnShellMenuDelete;
property OnShellMenuNewFolder;
property OnShellMenuPaste;
property OnShellMenuPasteLink;
property OnShellMenuPasteShortCut;
property OnShellMenuProperites;
property RenameMenuItem;
property PasteMenuItem;
property OnShow;
end;
{-------------------------------------------------------------------------------}
{ Exported Functions }
{-------------------------------------------------------------------------------}
// Rectange Functions
function RectWidth(ARect: TRect): integer;
function RectHeight(ARect: TRect): integer;
// PIDL Functions
function FindBrowseableRootPIDL(NS: TNamespace): PItemIDList;
function IsParentBrowseable(NS: TNamespace): Boolean;
function NamespaceToAbsolutePIDLArray(Namespaces: TNamespaceArray): TAbsolutePIDLArray;
function NamespaceToRelativePIDLArray(Namespaces: TNamespaceArray): TRelativePIDLArray;
function PathToPIDL(APath: WideString): PItemIDList;
function PIDLToPath(APIDL: PItemIDList): WideString;
function DirExistsVET(APath: WideString; ShowSystemMessages: Boolean): Boolean; overload;
function DirExistsVET(NS: TNamespace; ShowSystemMessages: Boolean): Boolean; overload;
procedure PIDLListQuickSort(PIDLList: TCommonPIDLList; const ParentFolder: IShellFolder; L, R: Integer);
procedure PIDLQuickSort(PIDLList: TPIDLArray; const ParentFolder: IShellFolder; L, R: Integer);
// Time Conversions
//** NOTE these are not WideString functions they will use ANSI strings internally
function ConvertLocalStrToTFileTime(LocalStr: WideString; var FileTime: TFileTime): Boolean;
function ConvertTFileTimeToLocalStr(AFileTime: TFILETIME): WideString;
function ConvertFileTimetoDateTime(AFileTime : TFileTime): TDateTime;
// Various Functions
function CreateSpecialNamespace(FolderID: integer): TNamespace;
function DefaultSystemImageIndex(FolderType: TDefaultFolderIcon): integer;
function FileIconInit(FullInit: BOOL): BOOL; stdcall;
function IENamespaceShown(PerUser: Boolean): Boolean;
function GUIDToInterfaceStr(riid: TGUID): String;
function CFM_FlagsToShellContextMenuFlags(Flags: DWORD): TShellContextMenuFlags;
function ClipboardContainsShellFormats: Boolean;
// IShellLink (ShortCut) helpers
function CreateShellLink(
ALnkFilePath,
ATargetFilePath: WideString;
AnArguments: WideString = '';
AWorkingDir: WideString = '';
ADescription: WideString = '';
AShowCmd: TCmdShow = swShowNormal;
AHotKey: Word = 0;
AHotKeyModifier: THotKeyModifiers = [];
AnIconLocation: WideString = '';
AnIconIndex: integer = 0
): Boolean;
function HotKeyModifiersToStr(HotKeyMod: THotKeyModifiers): WideString;
function PotentialMappedDrive(NS: TNamespace): Boolean;
function FileObjectsToFlags(FileObjects: TFileObjects): DWORD;
function FileObjectsToString(FileObjects: TFileObjects): WideString;
{$ifdef COMPILER_4}
procedure FreeAndNil(var Obj);
function Supports(const Instance: IUnknown; const Intf: TGUID; out Inst): Boolean;
{$endif}
function GetDiskFreeSpaceMP(Drive: PWideChar; var SectorsperCluster, BytesperSector, FreeClusters, TotalClusters: DWORD): boolean;
function DriveSize(Drive: PWideChar): Int64; overload;
function DriveSize(Drive: PWideChar; ByteSize: TBtyeSize): WideString; overload;
function DriveFreeSpace(Drive: PWideChar): Int64; overload;
function DriveFreeSpace(Drive: PWideChar; ByteSize: TBtyeSize): WideString; overload;
function IsSpecialVariable(TestPath: WideString; var NS: TNamespace): Boolean;
function SpecialVariableReplacePath(var Path: WideString): Boolean;
// function MPBrowseForFolder(Title, InitialPath: WideString; BrowseFlags: TMPBrowseFlags): WideString; overload;
function MPBrowseForFolder(Title, RootFolder, InitialPath: WideString; BrowseFlags: TMPBrowseFlags; var SelectedPath: WideString): Boolean; overload;
function MPBrowseForFolder(Title: WideString; RootFolder, InitialPath: PItemIDList; BrowseFlags: TMPBrowseFlags; var SelectedPath: PItemIDList): Boolean; overload;
// Merges a TVirtualShellPopupMenu object into a Shell Context Menu
{$IFDEF TNTSUPPORT}
function MergeMenuIntoContextMenu(Menu: TTntPopupMenu; ContextMenu: HMenu; Index: Integer; idStart: UINT): Integer;
{$ELSE}
function MergeMenuIntoContextMenu(Menu: TPopupMenu; ContextMenu: HMenu; Index: Integer; idStart: UINT): Integer;
{$ENDIF}
var
{ A few global common Namespaces to be used for various purposes. }
PIDLMgr: TCommonPIDLManager;
DesktopFolder,
RecycleBinFolder,
PhysicalDesktopFolder,
DrivesFolder,
HistoryFolder,
PrinterFolder,
ControlPanelFolder,
NetworkNeighborHoodFolder,
TemplatesFolder,
MyDocumentsFolder,
FavoritesFolder,
ProgramFilesFolder,
UserDocumentsFolder: TNamespace;
SHLimitInputEdit: TSHLimitInputEdit;
AnimateWindow: function(Wnd: HWND; dwTime: Cardinal; dwFlags: Cardinal): WordBool; stdcall;
// Fundamental Change in Version 2.0. Should be more correct in what shell dialogs
// are Modal and if they stay on top of the correct window.
MP_UseModalDialogs: Boolean = True; // Causes calls to GetUIIObject to use a parent window
MP_UseSpecialReparsePointOverlay: Boolean = False; // Shows an overlay on Reparse points
MP_ThreadedShellExecute: Boolean = False; // Causes shell Execute to be launched in a separate thread
implementation
uses
Dialogs;
type
TShellILIsParent = function(PIDL1: PItemIDList; PIDL2: PItemIDList;
ImmediateParent: LongBool): LongBool; stdcall;
TShellILIsEqual = function(PIDL1: PItemIDList; PIDL2: PItemIDList): LongBool; stdcall;
var
ShellILIsParent: TShellILIsParent;
ShellILIsEqual: TShellILIsEqual;
////////////////////////////////////////////////////////////////////////////////
// Global Functions
////////////////////////////////////////////////////////////////////////////////
function SpecialVariableReplacePath(var Path: WideString): Boolean;
function ReplacePath(Path, Variable, VarPath: WideString): WideString;
begin
Result := WideStringReplace(Path, Variable, VarPath, [rfReplaceAll, rfIgnoreCase]);
end;
var
OldPath: WideString;
begin
OldPath := Path;
// Psudo Variables
Path := ReplacePath(Path, '%sysdir%', WideLowerCase(WideStripTrailingBackslash(SystemDirectory)));
Path := ReplacePath(Path, '%temp%', WideLowerCase(WideStripTrailingBackslash(WideGetTempDir)));
Path := ReplacePath(Path, '%appdata%', WideLowerCase(WideStripTrailingBackslash(UserDocumentsFolder.NameForParsing)));
Path := ReplacePath(Path, '%favorites%', WideLowerCase(WideStripTrailingBackslash(FavoritesFolder.NameForParsing)));
Path := ReplacePath(Path, '%personal%', WideLowerCase(WideStripTrailingBackslash(MyDocumentsFolder.NameForParsing)));
Path := ReplacePath(Path, '%templates%', WideLowerCase(WideStripTrailingBackslash(TemplatesFolder.NameForParsing)));
Path := ReplacePath(Path, '%history%', WideLowerCase(WideStripTrailingBackslash(HistoryFolder.NameForParsing)));
Path := ReplacePath(Path, '%desktopfolder%', WideLowerCase(WideStripTrailingBackslash(PhysicalDesktopFolder.NameForParsing)));
// Environment variables
Path := ReplacePath(Path, '%userprofile%', WideStripTrailingBackslash(WideExpandEnviromentString('%USERPROFILE%')));
Path := ReplacePath(Path, '%allusersprofile%', WideStripTrailingBackslash(WideExpandEnviromentString('%ALLUSERSPROFILE%')));
Path := ReplacePath(Path, '%programfiles%', WideStripTrailingBackslash(WideExpandEnviromentString('%ProgramFiles%')));
Path := ReplacePath(Path, '%systemroot%', WideStripTrailingBackslash(WideExpandEnviromentString('%SystemRoot%')));
Path := ReplacePath(Path, '%systemdrive%', WideStripTrailingBackslash(WideExpandEnviromentString('%SystemDrive%')));
Path := ReplacePath(Path, '%windir%', WideStripTrailingBackslash(WideExpandEnviromentString('%windir%')));
Path := ReplacePath(Path, '%tmp%', WideStripTrailingBackslash(WideExpandEnviromentString('%TMP%')));
Path := ReplacePath(Path, '%temp%', WideStripTrailingBackslash(WideExpandEnviromentString('%TEMP%')));
Path := ReplacePath(Path, '%public%', WideStripTrailingBackslash(WideExpandEnviromentString('%PUBLIC%')));
Path := ReplacePath(Path, '%programdata%', WideStripTrailingBackslash(WideExpandEnviromentString('%ProgramData%')));
Path := ReplacePath(Path, '%homedrive%', WideStripTrailingBackslash(WideExpandEnviromentString('%HOMEDRIVE%')));
Path := ReplacePath(Path, '%homepath%', WideStripTrailingBackslash(WideExpandEnviromentString('%HOMEPATH%')));
Path := ReplacePath(Path, '%commonprogramfiles%', WideStripTrailingBackslash(WideExpandEnviromentString('%CommonProgramFiles%')));
Path := ReplacePath(Path, '%appdata%', WideStripTrailingBackslash(WideExpandEnviromentString('%APPDATA%')));
Result := OldPath <> Path
end;
function IsSpecialVariable(TestPath: WideString; var NS: TNamespace): Boolean;
var
PIDL: PItemIDList;
begin
NS := nil;
PIDL := nil;
if WideLowerCase(TestPath) = '%desktop%' then
PIDL := PIDLMgr.CopyPIDL(DesktopFolder.AbsolutePIDL)
else
if WideLowerCase(TestPath) = '%network%' then
PIDL := PIDLMgr.CopyPIDL(NetworkNeighborHoodFolder.AbsolutePIDL)
else
if WideLowerCase(TestPath) = '%printer%' then
PIDL := PIDLMgr.CopyPIDL(PrinterFolder.AbsolutePIDL)
else
if (WideLowerCase(TestPath) = '%drives%') or (WideLowerCase(TestPath) = '%mycomputer%') then
PIDL := PIDLMgr.CopyPIDL(DrivesFolder.AbsolutePIDL)
else begin
if SpecialVariableReplacePath(TestPath) then
if WideDirectoryExists(TestPath) then
PIDL := PathToPIDL(TestPath)
end;
if Assigned(PIDL) then
NS := TNamespace.Create(PIDL, nil)
else
if not Assigned(NS) and not WideDirectoryExists(TestPath) then
begin
// See if it a specially formated CLSID
PIDL := PathToPIDL(TestPath);
if Assigned(PIDL) then
NS := TNamespace.Create(PIDL, nil);
end;
Result := Assigned(NS)
end;
function GUIDToInterfaceStr(riid: TGUID): String;
begin
if IsEqualGUID(riid, IOleCommandTarget) then
Result := 'IOleCommandTarget'
else
if IsEqualGUID(riid, IID_IPersistFreeThreadedObject) then
Result := 'IPersistFreeThreadedObject'
else
if IsEqualGUID(riid, IShellView) then
Result := 'IShellView'
else
if IsEqualGUID(riid, IID_ICategoryProvider) then
Result := 'ICategoryProvider'
else
if IsEqualGUID(riid, IID_IQueryAssociations) then
Result := 'IQueryAssociations'
else
if IsEqualGUID(riid, IID_IAssociationArray) then
Result := 'IAssociationArray'
else
if IsEqualGUID(riid, IPersistIDList) then
Result := 'IPersistIDList'
else
if IsEqualGUID(riid, IID_IInternetSecurityManager) then
Result := 'IInternetSecurityManager'
else
if IsEqualGUID(riid, IObjectWithSite) then
Result := 'IObjectWithSite'
else
if IsEqualGUID(riid, IPersist) then
Result := 'IPersist'
else
if IsEqualGUID(riid, IPersistFolder) then
Result := 'IPersistFolder'
else
if IsEqualGUID(riid, IPersistFolder2) then
Result := 'IPersistFolder2'
else
if IsEqualGUID(riid, IShellFolder) then
Result := 'IShellFolder'
else
if IsEqualGUID(riid, IShellFolder2) then
Result := 'IShellFolder2'
else
if IsEqualGUID(riid, IShellFolderViewCB) then
Result := 'IShellFolderViewCB'
else
if IsEqualGUID(riid, IContextMenu) then
Result := 'IContextMenu'
else
if IsEqualGUID(riid, IContextMenu2) then
Result := 'IContextMenu2'
else
if IsEqualGUID(riid, IContextMenu3) then
Result := 'IContextMenu3'
(* else
{$IFDEF CPPB_6_UP}
if IsEqualGUID(riid, IBCB6ShellDetails) then
{$ELSE}
if IsEqualGUID(riid, IShellDetails) then
{$ENDIF}
Result := 'IShellDetails' *)
else
if IsEqualGUID(riid, IStream) then
Result := 'IStream'
else
if IsEqualGUID(riid, IDataObject) then
Result := 'IDataObject'
else
if IsEqualGUID(riid, IDropSource) then
Result := 'IDropSource'
else
if IsEqualGUID(riid, IDropTarget) then
Result := 'IDropTarget'
else
if IsEqualGUID(riid, IExtractIconA) then
Result := 'IExtractIconA'
else
if IsEqualGUID(riid, IExtractIconW) then
Result := 'IExtractIconW'
else
if IsEqualGUID(riid, IExtractImage) then
Result := 'IExtractImage'
else
if IsEqualGUID(riid, IQueryInfo) then
Result := 'IQueryInfo'
else
if IsEqualGUID(riid, IShellIcon) then
Result := 'IShellIcon'
else
if IsEqualGUID(riid, IBrowserFrameOptions) then
Result := 'IBrowserFrameOptions'
else
if IsEqualGUID(riid, IBindHost) then
Result := 'IBindHost'
else
if IsEqualGUID(riid, IID_IBindProtocol) then
Result := 'IBindProtocol'
else
if IsEqualGUID(riid, IClassFactory) then
Result := 'IClassFactory'
else
if IsEqualGUID(riid, IShellExtInit) then
Result := 'IShellExtInit'
else
if IsEqualGUID(riid, IShellLinkW) then
Result := 'IShellLinkW'
else
if IsEqualGUID(riid, IShellLinkA) then
Result := 'IShellLinkW'
else
Result := 'Unknown GUID: ' + GUIDToString(riid)
end;
function CFM_FlagsToShellContextMenuFlags(Flags: DWORD): TShellContextMenuFlags;
begin
Result := [];
if CMF_CANRENAME and Flags <> 0 then
Include(Result, cmfCanRename);
if CMF_CANRENAME and Flags <> 0 then
Include(Result, cmfCanRename);
// if CMF_DISABLEVERBS and Flags <> 0 then
// Include(Result, cmfDisableVerbs);
if CMF_EXPLORE and Flags <> 0 then
Include(Result, cmfExplore);
if CMF_INCLUDESTATIC and Flags <> 0 then
Include(Result, cmfIncludeStatic);
if CMF_NODEFAULT and Flags <> 0 then
Include(Result, cmfNoDefault);
if CMF_NORMAL and Flags <> 0 then
Include(Result, cmfNormal);
if CMF_NOVERBS and Flags <> 0 then
Include(Result, cmfNoVerbs);
if CMF_VERBSONLY and Flags <> 0 then
Include(Result, cmfVerbsOnly);
end;
function ClipboardContainsShellFormats: Boolean;
begin
Result := IsClipboardFormatAvailable(CF_SHELLIDLIST) or IsClipboardFormatAvailable(CF_HDROP)
end;
function MPBrowseForFolderCallback(Wnd: hWnd; uMsg: UINT; lParam: LPARAM; lpData: LPARAM): Integer; stdcall;
begin
case uMsg of
BFFM_INITIALIZED:
begin
// This is the PIDL of the starting folder
if lpData <> 0 then
SendMessage(Wnd, BFFM_SETSELECTION, 0, lpData);
end;
BFFM_SELCHANGED:
begin
end;
BFFM_VALIDATEFAILEDA:
begin
end;
BFFM_VALIDATEFAILEDW:
begin
end;
end;
Result := 0;
end;
function MPBrowseForFolder(Title: WideString; RootFolder, InitialPath: PItemIDList; BrowseFlags: TMPBrowseFlags; var SelectedPath: PItemIDList): Boolean;
function FlagsToTMPBrowseFlags(Flags: TMPBrowseFlags): DWORD;
begin
Result := 0;
if mpbfComputers in Flags then
Result := Result or BIF_BROWSEFORCOMPUTER;
if mpbfPrinters in Flags then
Result := Result or BIF_BROWSEFORPRINTER;
if mpbfDontGoBelowDomain in Flags then
Result := Result or BIF_DONTGOBELOWDOMAIN;
if mpbfFileSysAncestors in Flags then
Result := Result or BIF_RETURNFSANCESTORS;
if mpbfFileSysFolder in Flags then
Result := Result or BIF_RETURNONLYFSDIRS;
// if mpbfIncludeStatusText in Flags then
// Result := Result or BIF_STATUSTEXT;
if mpbfIncludeFiles in Flags then
Result := Result or BIF_BROWSEINCLUDEFILES;
if mpbfNewStyleDialog in Flags then
Result := Result or BIF_NEWDIALOGSTYLE;
if mpbfEditBox in Flags then
Result := Result or BIF_EDITBOX;
if mpbfIncludeURLs in Flags then
Result := Result or BIF_BROWSEINCLUDEURLS;
if mpbfSharable in Flags then
Result := Result or BIF_SHAREABLE;
if mpbfMustExist in Flags then
Result := Result or BIF_VALIDATE;
end;
var
BrowseInfoW: TBrowseInfoW;
BrowseInfoA: TBrowseInfoA;
DisplayNameA: array [0..MAX_PATH] of Char;
DisplayNameW: array [0..MAX_PATH] of WideChar;
begin
if IsUnicode then
begin
FillChar(BrowseInfoW, SizeOf(BrowseInfoW), #0);
BrowseInfoW.hwndOwner := GetActiveWindow;
BrowseInfoW.pidlRoot := RootFolder;
BrowseInfoW.lParam := Integer( InitialPath);
BrowseInfoW.pszDisplayName := DisplayNameW;
{$IFDEF CPPB}
BrowseInfoW.lpfn := MPBrowseForFolderCallback;
{$ELSE}
BrowseInfoW.lpfn := @MPBrowseForFolderCallback;
{$ENDIF}
BrowseInfoW.lpszTitle := PWideChar(Title);
BrowseInfoW.ulFlags := FlagsToTMPBrowseFlags(BrowseFlags);
SelectedPath := SHBrowseForFolderW_MP(BrowseInfoW);
end else
begin
FillChar(BrowseInfoA, SizeOf(BrowseInfoA), #0);
BrowseInfoA.hwndOwner := GetActiveWindow;
BrowseInfoW.pidlRoot := RootFolder;
BrowseInfoA.lParam := Integer( InitialPath);
BrowseInfoA.pszDisplayName := DisplayNameA;
{$IFDEF CPPB}
BrowseInfoA.lpfn := MPBrowseForFolderCallback;
{$ELSE}
BrowseInfoA.lpfn := @MPBrowseForFolderCallback;
{$ENDIF}
BrowseInfoA.lpszTitle := PChar(string(Title));
BrowseInfoA.ulFlags := FlagsToTMPBrowseFlags(BrowseFlags);
SelectedPath := SHBrowseForFolderA(BrowseInfoA);
end;
Result := Assigned(SelectedPath)
end;
function MPBrowseForFolder(Title, RootFolder, InitialPath: WideString; BrowseFlags: TMPBrowseFlags; var SelectedPath: WideString): Boolean; overload;
var
NS: TNamespace;
RootPIDL, InitialPathPIDL, ReturnPIDL: PItemIDList;
begin
RootPIDL := PathToPIDL(RootFolder);
InitialPathPIDL := PathToPIDL(InitialPath);
ReturnPIDL := nil;
Result := MPBrowseForFolder(Title, RootPIDL, InitialPathPIDL, BrowseFlags, ReturnPIDL);
if Result then
begin
NS := TNamespace.Create(ReturnPIDL, nil);
SelectedPath := NS.NameParseAddress;
NS.Free;
end;
PIDLMgr.FreePIDL(RootPIDL);
PIDLMgr.FreePIDL(InitialPathPIDL);
end;
{$IFDEF TNTSUPPORT}
function MergeMenuIntoContextMenu(Menu: TTntPopupMenu; ContextMenu: HMenu; Index: Integer; idStart: UINT): Integer;
var
i: Integer;
begin
Result := -1;
if Assigned(Menu) and (ContextMenu <> 0) then
begin
Result := idStart;
for i := Menu.Items.Count - 1 downto 0 do
begin
AddContextMenuItem(ContextMenu, Menu.Items[i].Caption, Index, Result);
Inc(Result);
if Menu.Items[i].Count > 0 then
beep;
end
end
end;
{$ELSE}
function MergeMenuIntoContextMenu(Menu: TPopupMenu; ContextMenu: HMenu; Index: Integer; idStart: UINT): Integer;
var
i: Integer;
begin
Result := -1;
if Assigned(Menu) and (ContextMenu <> 0) then
begin
Result := idStart;
for i := Menu.Items.Count - 1 downto 0 do
begin
AddContextMenuItem(ContextMenu, Menu.Items[i].Caption, Index, Result);
Inc(Result);
if Menu.Items[i].Count > 0 then
beep;
end
end
end;
{$ENDIF}
// PIDL Functions
{ ----------------------------------------------------------------------------- }
function NamespaceToRelativePIDLArray(Namespaces: TNamespaceArray): TRelativePIDLArray;
var
i: integer;
begin
Result := nil;
if Assigned(Namespaces) then
begin
SetLength(Result, Length(Namespaces));
for i := 0 to Length(Namespaces) - 1 do
Result[i] := Namespaces[i].RelativePIDL;
end
end;
function FindBrowseableRootPIDL(NS: TNamespace): PItemIDList;
var
Found: Boolean;
begin
Result := nil;
Found := False;
if Assigned(NS) then
begin
if not NS.Folder then
NS := NS.Parent;
if IsParentBrowseable(NS) then
begin
while not Found and not NS.IsDesktop do
begin
if NS.Browsable then
Found := True
else
NS := NS.Parent
end
end;
Result := PIDLMgr.CopyPIDL(NS.AbsolutePIDL)
end
end;
function IsParentBrowseable(NS: TNamespace): Boolean;
begin
Result := False;
if Assigned(NS) then
begin
while not Result and not NS.IsDesktop do
begin
Result := NS.Browsable;
if not Result then
NS := NS.Parent
end
end
end;
function NamespaceToAbsolutePIDLArray(Namespaces: TNamespaceArray): TAbsolutePIDLArray;
var
i: integer;
begin
Result := nil;
if Assigned(Namespaces) then
begin
SetLength(Result, Length(Namespaces));
for i := 0 to Length(Namespaces) - 1 do
Result[i] := Namespaces[i].AbsolutePIDL;
end
end;
{ ----------------------------------------------------------------------------- }
function PathToPIDL(APath: WideString): PItemIDList;
// Takes the passed Path and attempts to convert it to the equavalent PIDL
var
Desktop: IShellFolder;
pchEaten, dwAttributes: ULONG;
begin
Result := nil;
begin
SHGetDesktopFolder(Desktop);
dwAttributes := 0;
if Assigned(Desktop) then
Desktop.ParseDisplayName(0, nil, PWideChar(APath), pchEaten, Result, dwAttributes)
end
end;
{ ----------------------------------------------------------------------------- }
{ ----------------------------------------------------------------------------- }
function PIDLToPath(APIDL: PItemIDList): WideString;
var
Folder: TNamespace;
begin
Result := '';
Folder := TNamespace.Create(APIDL, nil);
try
Folder.FreePIDLOnDestroy := False;
if Assigned(Folder) then
Result := Folder.NameForParsing;
finally
Folder.Free
end
end;
{ ----------------------------------------------------------------------------- }
function IENamespaceShown(PerUser: Boolean): Boolean;
var
Reg: TRegistry;
begin
Result := True;
Reg := TRegistry.Create;
try
if PerUser then
Reg.RootKey := HKEY_CURRENT_USER
else
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.KeyExists('Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel') then
begin
if Reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel', False) then
begin
if Reg.ValueExists('{871C5380-42A0-1069-A2EA-08002B30309D}') then
begin
Result := Reg.ReadInteger('{871C5380-42A0-1069-A2EA-08002B30309D}') = 0;
end
end
end;
finally
Reg.Free
end;
end;
{ ----------------------------------------------------------------------------- }
function DirExistsVET(APath: WideString; ShowSystemMessages: Boolean): Boolean; overload;
const
FLAGS = SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN;
var
Desktop, Folder: IShellFolder;
TempPIDL, PIDL: PItemIDList;
EnumIDList: IEnumIDList;
hWndOwner: THandle;
TempPath: WideString;
begin
Result := False;
PIDL := nil;
if ShowSystemMessages then
hWndOwner := GetActiveWindow //Application.Handle
else
hWndOwner := 0;
TempPath := WideExtractFileDrive(APath) + '\';
// First make sure the drive is available, it may be a remoted password protected drive
TempPIDL := PathToPIDL(TempPath);
if Succeeded(SHGetDesktopFolder(Desktop)) then
if Succeeded(Desktop.BindToObject(TempPIDL, nil, IShellFolder, Pointer(Folder))) then
if Folder.EnumObjects(hWndOwner, FLAGS, EnumIDList)= NOERROR then
begin
PIDL := PathToPIDL(APath);
if Succeeded(Desktop.BindToObject(PIDL, nil, IShellFolder, Pointer(Folder))) then
Result := Folder.EnumObjects(hWndOwner, FLAGS, EnumIDList)= NOERROR
end;
coTaskMemFree(TempPIDL);
if Assigned(PIDL) then
coTaskMemFree(PIDL);
end;
{ ----------------------------------------------------------------------------- }
{ ----------------------------------------------------------------------------- }
function DirExistsVET(NS: TNamespace; ShowSystemMessages: Boolean): Boolean; overload;
begin
Result := DirExistsVET(NS.NameForParsing, ShowSystemMessages)
end;
{ ----------------------------------------------------------------------------- }
function RectWidth(ARect: TRect): integer;
begin
Result := ARect.Right - ARect.Left
end;
{ ----------------------------------------------------------------------------- }
{ ----------------------------------------------------------------------------- }
function RectHeight(ARect: TRect): integer;
begin
Result := ARect.Bottom - ARect.Top
end;
{ ----------------------------------------------------------------------------- }
function PotentialMappedDrive(NS: TNamespace): Boolean;
// A mapped drive will not return valid information, other then
// its display name under some conditions so always try it.
var
DriveType: DWORD;
begin
Result := False;
if WideIsDrive(NS.NameForParsing) then
begin
if Assigned(GetDriveTypeW_MP) then
DriveType := GetDriveTypeW_MP(PWideChar(NS.NameForParsing))
else
DriveType := GetDriveType(PChar(string(NS.NameForParsing)));
Result := (DriveType = DRIVE_NO_ROOT_DIR) or (DriveType = DRIVE_REMOTE)
end
end;
{ ----------------------------------------------------------------------------- }
function FileObjectsToFlags(FileObjects: TFileObjects): DWORD;
begin
Result := 0;
if foFolders in FileObjects then
Result := Result or SHCONTF_FOLDERS;
if foNonFolders in FileObjects then
Result := Result or SHCONTF_NONFOLDERS;
if foHidden in FileObjects then
Result := Result or SHCONTF_INCLUDEHIDDEN;
if IsUnicode and not IsWinNT4 then
begin
if foShareable in FileObjects then
Result := Result or SHCONTF_SHAREABLE;
if foNetworkPrinters in FileObjects then
Result := Result or SHCONTF_NETPRINTERSRCH;
end;
end;
function FileObjectsToString(FileObjects: TFileObjects): WideString;
begin
Result := '';
if foFolders in FileObjects then
Result := Result + 'Folders, ';
if foNonFolders in FileObjects then
Result := Result + 'NonFolders, ';
if foHidden in FileObjects then
Result := Result + 'Hidden, ';
if foShareable in FileObjects then
Result := Result + 'Shareable, ';
if foNetworkPrinters in FileObjects then
Result := Result + 'NetworkPrinters, ';
if Length(Result) > 0 then
SetLength(Result, Length(Result) - 2)
end;
// Time Conversions
{ ----------------------------------------------------------------------------- }
// ANSI
function ConvertLocalStrToTFileTime(LocalStr: WideString;
var FileTime: TFileTime): Boolean;
var
SystemTime: TSystemTime;
begin
Result := True;
try
DateTimeToSystemTime(StrToDateTime(LocalStr), SystemTime)
except
on EConvertError do Result := False;
end;
if Result then
Result := SystemTimeToFileTime(SystemTime, FileTime);
end;
{ ----------------------------------------------------------------------------- }
function ValidFileTime(FileTime: TFileTime): Boolean;
begin
Result := (FileTime.dwLowDateTime <> 0) or (FileTime.dwHighDateTime <> 0);
end;
{ ----------------------------------------------------------------------------- }
// Converts a TFileTime structure into a local Time/Date String. This requires
// a check to make sure the TFileTime structure contains some info through
// the local function ValidFileTime then trying to convert the UTC time to Local
// UTC time. Then finally changing the UTC time to System time.
// ANSI/
function ConvertTFileTimeToLocalStr(AFileTime: TFILETIME): WideString;
var
SysTime: TSystemTime;
LocalFileTime: TFILETIME;
begin
if ValidFileTime(AFileTime)
and FileTimeToLocalFileTime(AFileTime, LocalFileTime)
and FileTimeToSystemTime(LocalFileTime, SysTime) then
try
Result := DateTimeToStr(SystemTimeToDateTime(SysTime))
except
Result := '';
end
else
Result := '';
end;
{ ----------------------------------------------------------------------------- }
function ConvertFileTimetoDateTime(AFileTime : TFileTime): TDateTime;
var
SysTime: TSystemTime;
LocalFileTime: TFILETIME;
begin
if ValidFileTime(AFileTime)
and FileTimeToLocalFileTime(AFileTime, LocalFileTime)
and FileTimeToSystemTime(LocalFileTime, SysTime) then
try
Result := SystemTimeToDateTime(SysTime);
except
Result := 0;
end
else
Result := 0;
end;
// Various Functions
{ ----------------------------------------------------------------------------- }
function CreateSpecialNamespace(FolderID: integer): TNamespace;
{ Creates a TNamespace based on the SpecialFolders defined by }
{ SHGetSpecialFolderLocation. }
var
PIDL: PItemIDList;
F: IShellFolder;
begin
SHGetspecialFolderLocation(0, FolderID, PIDL);
if Assigned(PIDL) then
begin
Result := TNamespace.Create(PIDL, nil);
F := Result.ParentShellFolder // just force the namespace to have Parent
end else
Result := nil
end;
{ ----------------------------------------------------------------------------- }
{ ----------------------------------------------------------------------------- }
function DefaultSystemImageIndex(FolderType: TDefaultFolderIcon): integer;
{ Extracts the default Icon for the given folder type passed to it. }
var
FileInfoA: TSHFileInfo;
FileInfoW: TSHFileInfoW;
FileExampleW: WideString;
FileExampleA: string;
Attrib, Flags: DWORD;
PIDL: PItemIDList;
NS: TNamespace;
begin
Result := -1;
Attrib := 0;
Flags := 0;
case FolderType of
diNormalFolder:
begin
FileExampleW := '*.*';
Attrib := FILE_ATTRIBUTE_DIRECTORY;
Flags := SHGFI_USEFILEATTRIBUTES or SHGFI_SHELLICONSIZE or SHGFI_SYSICONINDEX
end;
diOpenFolder:
begin
FileExampleW := '*.*';
Attrib := FILE_ATTRIBUTE_DIRECTORY;
Flags := SHGFI_USEFILEATTRIBUTES or SHGFI_SHELLICONSIZE or SHGFI_SYSICONINDEX or SHGFI_OPENICON
end;
diUnknownFile:
begin
FileExampleW := '*.zyxwv';
Attrib := FILE_ATTRIBUTE_NORMAL;
Flags := SHGFI_USEFILEATTRIBUTES or SHGFI_SHELLICONSIZE or SHGFI_SYSICONINDEX
end;
diLink:
begin
FileExampleW := '';
Result := SHORTCUT_ICON_INDEX;
end;
diMyDocuments:
begin
if Assigned(DesktopFolder) then
begin
PIDL := DesktopFolder.ParseDisplayName('::{450d8fba-ad25-11d0-98a8-0800361b1103}');
if Assigned(PIDL) then
begin
NS := TNamespace.Create(PIDL, nil);
Result := NS.GetIconIndex(False, icSmall, True);
NS.Free
end
end else
Result := DefaultSystemImageIndex(diNormalFolder)
end
else
FileExampleW := ''
end;
if FileExampleW <> '' then
begin
if IsUnicode then
begin
FillChar(FileInfoW, SizeOf(FileInfoW), #0);
SHGetFileInfoW_MP(PWideChar(FileExampleW), Attrib, FileInfoW, SizeOf(TSHFileInfoW), Flags);
Result := FileInfoW.iIcon;
end else
begin
FileExampleA := FileExampleW;
FillChar(FileInfoA, SizeOf(FileInfoA), #0);
SHGetFileInfoA(PChar(FileExampleA), Attrib, FileInfoA, SizeOf(TSHFileInfoA), Flags);
Result := FileInfoA.iIcon;
end
end
end;
{ ----------------------------------------------------------------------------- }
{ ----------------------------------------------------------------------------- }
// Forces the correct icons for the Common Program Groups on Windows NT 4.0.
// Borrowed from John T and GXExplorer <g>
function FileIconInit(FullInit: BOOL): BOOL; stdcall;
type
TFileIconInit = function(FullInit: BOOL): BOOL; stdcall;
var
ShellDLL: HMODULE;
PFileIconInit: TFileIconInit;
begin
Result := False;
if (Win32Platform = VER_PLATFORM_WIN32_NT) then
begin
ShellDLL := GetModuleHandle(PChar(Shell32));
// ShellDLL := LoadLibrary(PChar(Shell32));
PFileIconInit := GetProcAddress(ShellDLL, PChar(660));
if (Assigned(PFileIconInit)) then
Result := PFileIconInit(FullInit);
end;
end;
{ ----------------------------------------------------------------------------- }
// IShellLink (ShortCut) helpers
{ ----------------------------------------------------------------------------- }
function CreateShellLink(ALnkFilePath, ATargetFilePath: WideString; AnArguments: WideString = '';
AWorkingDir: WideString = ''; ADescription: WideString = ''; AShowCmd: TCmdShow = swShowNormal;
AHotKey: Word = 0; AHotKeyModifier: THotKeyModifiers = []; AnIconLocation: WideString = '';
AnIconIndex: integer = 0): Boolean;
var
ShellLink: TVirtualShellLink;
begin
Result := True;
ShellLink := TVirtualShellLink.Create(nil);
if Assigned(ShellLink) then
try
try
ShellLink.FileName := ALnkFilePath;
ShellLink.TargetPath := ATargetFilePath;
if AnArguments <> '' then
ShellLink.Arguments := AnArguments;
if AWorkingDir <> '' then
ShellLink.WorkingDirectory := AWorkingDir;
if ADescription <> '' then
ShellLink.Description := ADescription;
if AShowCmd <> swShowNormal then
ShellLink.ShowCmd := AShowCmd;
if (AHotKey <> 0) then
ShellLink.HotKey := AHotKey;
if AHotKeyModifier <> [] then
ShellLink.HotKeyModifiers := AHotKeyModifier;
if AnIconLocation <> '' then
ShellLink.IconLocation := AnIconLocation;
if AnIconIndex <> 0 then
ShellLink.IconIndex := AnIconIndex;
ShellLink.WriteLink(ShellLink.FileName);
except
Result := False;
raise;
end
finally
ShellLink.Free
end
end;
{ ----------------------------------------------------------------------------- }
{ ----------------------------------------------------------------------------- }
function HotKeyModifiersToStr(HotKeyMod: THotKeyModifiers): WideString;
begin
Result := '';
if hkmAlt in HotKeyMod then
Result := Result + ' Alt';
if hkmControl in HotKeyMod then
Result := Result + ' Control';
if hkmExtendedKey in HotKeyMod then
Result := Result + ' ExtendedKey';
if hkmShift in HotKeyMod then
Result := Result + ' Shift';
end;
{ ----------------------------------------------------------------------------- }
{ Some Stuff D4 lacks.
}
{$ifdef COMPILER_4}
{ ----------------------------------------------------------------------------- }
procedure FreeAndNil(var Obj);
var
P: TObject;
begin
P := TObject(Obj);
TObject(Obj) := nil;
P.Free;
end;
{ ----------------------------------------------------------------------------- }
{ ----------------------------------------------------------------------------- }
function Supports(const Instance: IUnknown; const Intf: TGUID; out Inst): Boolean;
begin
Result := (Instance <> nil) and (Instance.QueryInterface(Intf, Inst) = 0);
end;
{ ----------------------------------------------------------------------------- }
{$endif}
////////////////////////////////////////////////////////////////////////////////
// Local Functions
////////////////////////////////////////////////////////////////////////////////
procedure PIDLQuickSort(PIDLList: TPIDLArray; const ParentFolder: IShellFolder; L, R: Integer);
var
I, J: Integer;
P, T: PItemIDList;
begin
if L < R then
repeat
I := L;
J := R;
P := PIDLList[(L + R) shr 1];
repeat
while ShortInt(ParentFolder.CompareIDs(0, PIDLList[I], P)) < 0 do
Inc(I);
while ShortInt(ParentFolder.CompareIDs(0, PIDLList[J], P)) > 0 do
Dec(J);
if I <= J then
begin
T := PIDLList[I];
PIDLList[I] := PIDLList[J];
PIDLList[J] := T;
Inc(I);
Dec(J);
end;
until I > J;
if L < J then
PIDLQuickSort(PIDLList, ParentFolder, L, J);
L := I;
until I >= R;
end;
procedure PIDLListQuickSort(PIDLList: TCommonPIDLList; const ParentFolder: IShellFolder; L, R: Integer);
var
I, J: Integer;
P, T: PItemIDList;
begin
if L < R then
repeat
I := L;
J := R;
P := PIDLList[(L + R) shr 1];
repeat
while ShortInt(ParentFolder.CompareIDs(0, PIDLList[I], P)) < 0 do
Inc(I);
while ShortInt(ParentFolder.CompareIDs(0, PIDLList[J], P)) > 0 do
Dec(J);
if I <= J then
begin
T := PIDLList[I];
PIDLList[I] := PIDLList[J];
PIDLList[J] := T;
Inc(I);
Dec(J);
end;
until I > J;
if L < J then
PIDLListQuickSort(PIDLList, ParentFolder, L, J);
L := I;
until I >= R;
end;
{ ----------------------------------------------------------------------------- }
function RequestedDragEffect(grfKeyState: integer): HResult;
{ Looks at the KeyState during a IDragTarget notification. The return value}
{ is the expected behavior common in Windows. Note this does not mean that }
{ the DragSource is actually capable of this action. }
begin
// Strip off the mouse button information keep only Ctrl and Shift information
grfKeyState := grfKeyState and (MK_CONTROL or MK_SHIFT);
// Standard Windows Shell behavior
if grfKeyState = 0 then Result := DROPEFFECT_MOVE // Windows default
else
if grfKeyState = MK_CONTROL then Result := DROPEFFECT_COPY
else
if grfKeyState = (MK_CONTROL or MK_SHIFT) then Result := DROPEFFECT_LINK
else
Result := DROPEFFECT_NONE;
end;
{ ----------------------------------------------------------------------------- }
{ ----------------------------------------------------------------------------- }
{ Thank you Angus Johnson for this article in UNDO }
{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
//Structures used in GetDiskFreeSpaceFAT32
{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
type
//DeviceIoControl registers structure...
TDevIoCtl_Registers = packed record
Reg_EBX : DWord;
Reg_EDX : DWord;
Reg_ECX : DWord;
Reg_EAX : DWord;
Reg_EDI : DWord;
Reg_ESI : DWord;
Reg_Flags : DWord;
end;
//Structure passed in Get_ExtFreeSpace ...
TExtGetDskFreSpcStruc = packed record
ExtFree_Size : Word;
ExtFree_Level : Word;
ExtFree_SectorsPerCluster : Integer;
ExtFree_BytesPerSector : Integer;
ExtFree_AvailableClusters : Integer;
ExtFree_TotalClusters : Integer;
ExtFree_AvailablePhysSectors : Integer;
ExtFree_TotalPhysSectors : Integer;
ExtFree_AvailableAllocationUnits : Integer;
ExtFree_TotalAllocationUnits : Integer;
ExtFree_Rsvd : array [0..1] of Integer;
end;
{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
//Angus Johnson's Delphi implimentation of - Int 21h Function 7303h Get_ExtFreeSpace (FAT32)
{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
function GetDiskFreeSpaceFAT32(Drive: PChar; var SectorsperCluster,
BytesperSector, FreeClusters, TotalClusters: DWORD): boolean;
const
VWIN32_DIOC_DOS_IOCTL = 6;
var
DevIoHandle : THandle;
BytesReturned : DWord;
Reg : TDevIoCtl_Registers;
ExtGetDskFreSpcStruc: TExtGetDskFreSpcStruc;
begin
result := false;
FillChar(ExtGetDskFreSpcStruc, sizeof(TExtGetDskFreSpcStruc),0);
FillChar(Reg, sizeof(TDevIoCtl_Registers),0);
with Reg do begin
reg_EAX := $7303;
reg_EDX := DWord(Drive); //DS:DX
Reg_EDI := DWord(@ExtGetDskFreSpcStruc); //ES:DI
reg_ECX := sizeof(TExtGetDskFreSpcStruc);
reg_Flags := 1; //set carry flag to assume error.
end;
if IsUnicode then
DevIoHandle := CreateFileW_MP( '\\.\vwin32', Generic_Read,
File_Share_Read or File_Share_Write, nil, Open_Existing, File_Attribute_Normal, 0)
else
DevIoHandle := CreateFile( '\\.\vwin32', Generic_Read,
File_Share_Read or File_Share_Write, nil, Open_Existing, File_Attribute_Normal, 0);
if DevIoHandle <> Invalid_Handle_Value then begin
result := DeviceIoControl(DevIoHandle, VWIN32_DIOC_DOS_IOCTL,
@Reg, SizeOf(Reg), @Reg, SizeOf(Reg), BytesReturned, nil);
CloseHandle(DevIoHandle);
if not result then
begin
exit //error
end
else if (Reg.reg_Flags and 1 <> 0) then begin
result := false; //If carry flag not cleared then => error
exit;
end
else with ExtGetDskFreSpcStruc do begin
BytesperSector := ExtFree_BytesPerSector;
SectorsperCluster := ExtFree_SectorsPerCluster;
TotalClusters := ExtFree_TotalClusters;
FreeClusters := ExtFree_AvailableClusters;
result := true;
end;
end;
end; {GetDiskFreeSpaceFAT32}
{ ----------------------------------------------------------------------------- }
function GetDiskFreeSpaceMP(Drive: PWideChar; var SectorsperCluster,
BytesperSector, FreeClusters, TotalClusters: DWORD): boolean;
begin
if Assigned(GetDiskFreeSpaceW_MP) then
Result := GetDiskFreeSpaceW_MP(
PWideChar( Drive), SectorsPerCluster, BytesPerSector, FreeClusters, TotalClusters)
else
if not IsWin95_SR1 then
Result := GetDiskFreeSpaceFAT32(
PChar( string(Drive)), SectorsPerCluster, BytesPerSector, FreeClusters, TotalClusters)
else
Result := GetDiskFreeSpaceA(
PChar( string(Drive)), SectorsPerCluster, BytesPerSector, FreeClusters, TotalClusters);
end;
function DriveSize(Drive: PWideChar): Int64;
var
SectorsperCluster, BytesperSector, FreeClusters, TotalClusters: DWORD;
begin
Result := 0;
if GetDiskFreeSpaceMP(Drive, SectorsperCluster, BytesperSector, FreeClusters, TotalClusters) then
Result := Int64(BytesperSector) * Int64(SectorsperCluster) * Int64(TotalClusters)
end;
function DriveSize(Drive: PWideChar; ByteSize: TBtyeSize): WideString;
begin
case ByteSize of
bsKiloBytes: Result := Format('%0.0n '+ 'KB', [DriveSize(Drive) / 1024]);
bsMegaBytes: Result := Format('%0.0n '+ 'MB', [DriveSize(Drive) / 1048576]);
bsGigiBytes: Result := Format('%0.1n '+ 'GB', [DriveSize(Drive) / 1073741824]);
bsTereBytes: Result := Format('%0.1n '+ 'TB', [DriveSize(Drive) / 1099511627776]);
end
end;
function DriveFreeSpace(Drive: PWideChar): Int64;
var
SectorsperCluster, BytesperSector, FreeClusters, TotalClusters: DWORD;
begin
Result := 0;
if GetDiskFreeSpaceMP(Drive, SectorsperCluster, BytesperSector, FreeClusters, TotalClusters) then
Result := Int64(BytesperSector) * Int64(SectorsperCluster) * Int64(FreeClusters)
end;
function DriveFreeSpace(Drive: PWideChar; ByteSize: TBtyeSize): WideString;
begin
case ByteSize of
bsKiloBytes: Result := Format('%0.0n '+ 'KB', [DriveFreeSpace(Drive) / 1024]);
bsMegaBytes: Result := Format('%0.0n '+ 'MB', [DriveFreeSpace(Drive) / 1048576]);
bsGigiBytes: Result := Format('%0.1n '+ 'GB', [DriveFreeSpace(Drive) / 1073741824]);
bsTereBytes: Result := Format('%0.1n '+ 'TB', [DriveFreeSpace(Drive) / 1099511627776]);
end
end;
{ ----------------------------------------------------------------------------- }
function LoadShell32Functions: Boolean;
var
ShellDLL: HMODULE;
begin
{ Don't see a point in making this all WideString compatible }
ShellDLL := GetModuleHandle(PChar(Shell32));
// ShellDLL := LoadLibrary(PChar(Shell32));
if ShellDll <> 0 then
begin
AnimateWindow := GetProcAddress(GetModuleHandle('user32'), 'AnimateWindow');
ShellILIsEqual := GetProcAddress(ShellDLL, PChar(21));
ShellILIsParent := GetProcAddress(ShellDLL, PChar(23));
SHLimitInputEdit := GetProcAddress(ShellDLL, PChar(747));
Result := Assigned(ShellILIsEqual) and Assigned(ShellILIsParent)
end else
Result := False;
end;
{ ----------------------------------------------------------------------------- }
{ TNamespace }
function TNamespace.CanCopyAll(NamespaceArray: TNamespaceArray): Boolean;
var
i: integer;
begin
if Assigned(NamespaceArray) then
begin
Result := True;
i := 0;
while Result and (i < Length(NamespaceArray)) do
begin
Result := NamespaceArray[i].CanCopy;
Inc(i)
end
end else
Result := False
end;
function TNamespace.CanCutAll(NamespaceArray: TNamespaceArray): boolean;
begin
Result := CanDeleteAll(NamespaceArray)
end;
function TNamespace.CanDeleteAll(NamespaceArray: TNamespaceArray): Boolean;
var
i: integer;
begin
if Assigned(NamespaceArray) then
begin
Result := True;
i := 0;
while Result and (i < Length(NamespaceArray)) do
begin
Result := NamespaceArray[i].CanDelete;
Inc(i)
end
end else
Result := False
end;
function TNamespace.CanPasteToAll(NamespaceArray: TNamespaceArray): Boolean;
begin
Result := False;
if Assigned(NamespaceArray) then
if Length(NamespaceArray) > 0 then
Result := True // Can try to paste to anything?
end;
function TNamespace.CanShowPropertiesOfAll(NamespaceArray: TNamespaceArray): Boolean;
var
i: integer;
begin
if Assigned(NamespaceArray) then
begin
Result := True;
i := 0;
while Result and (i < Length(NamespaceArray)) do
begin
Result := NamespaceArray[i].HasPropSheet;
Inc(i)
end
end else
Result := False
end;
function TNamespace.Clone(ReleasePIDLOnDestroy: Boolean): TNameSpace;
begin
// This is not really a true clone since we don't copy the parent, but it is
// dangerous to do that. Be careful using this function since things can
// potentially change in the shell.
Result := TNamespace.Create(PIDLMgr.CopyPIDL(AbsolutePIDL), nil);
Result.FreePIDLOnDestroy := ReleasePIDLOnDestroy;
end;
function TNamespace.ComparePIDL(PIDLToCompare: PItemIDList;
IsAbsolutePIDL: Boolean; Column: Integer = 0): ShortInt;
// Encapsulation of the CompareID Function of IShellFolder
// Returns > 0 if PIDLToCompare > RelativePIDL
// 0 if PIDLToCompare = RelativePIDL
// < 0 if PIDLToCompare < RelativePIDL
var
PIDL: PItemIDList;
begin
if Assigned(PIDLToCompare) then
begin
if Column < 0 then
Column := 0;
if PIDLMgr.IsDesktopFolder(PIDLToCompare) and IsDesktop then
Result := 0
else begin
PIDL := PIDLMgr.GetPointerToLastID(PIDLToCompare);
if Assigned(ParentShellFolder) then
begin
if IsAbsolutePIDL then
begin
Result := -1;
// First test is if the PIDL length is the same
if PIDLMgr.IDCount(PIDLToCompare) = PIDLMgr.IDCount(AbsolutePIDL) then
begin
if Assigned(Parent) then
begin
// Desktop items won't have a valid parent
if ILIsParent(Parent.AbsolutePIDL, PIDLToCompare, True) then
Result := ShortInt(ParentShellFolder.CompareIDs(Column, PIDL, RelativePIDL))
end else
Result := ShortInt(ParentShellFolder.CompareIDs(Column, PIDL, RelativePIDL))
end
end else
Result := ShortInt(ParentShellFolder.CompareIDs(Column, PIDL, RelativePIDL));
end else
Result := 0
end
end else
Result := -1 // If the pidl is not assigned then we clearly are greater!
end;
function TNamespace.ContextMenuItemHelp(MenuItemID: LongWord): WideString;
const
BufferLen = 128;
var
S: string;
Found: Boolean;
P: Pointer;
begin
Found := False;
if Assigned(CurrentContextMenu) and (MenuItemID <> $FFFFFFFF) and (MenuItemID > 0)then
begin
if IsUnicode then
begin
SetLength(Result, BufferLen);
{ Keep D6 from complaining about suspicious PChar cast }
P := @Result[1];
Found := CurrentContextMenu.GetCommandString(MenuItemID-1, GCS_HELPTEXTW, nil, PChar(P),
BufferLen) = NOERROR
end;
if not Found then
begin
SetLength(S, BufferLen);
if CurrentContextMenu.GetCommandString(MenuItemID-1, GCS_HELPTEXTA, nil, PChar(S),
BufferLen) <> NOERROR
then
Result := ''
else begin
SetLength(S, StrLen( PChar(S)));
Result := S
end
end else
SetLength(Result, lstrlenW(PWideChar( Result)))
end;
end;
function TNamespace.ContextMenuVerb(MenuItemID: Longword): WideString;
{ Returns the cononical (or not) verb that is equal to the MenuItemID, which is }
{ the HMenu identifer for a menu item. }
const
BufferLen = 128;
var
S: string;
Found: Boolean;
P: Pointer;
begin
Found := False;
if Assigned(CurrentContextMenu) and (MenuItemID <> $FFFFFFFF) and (MenuItemID > 0) then
begin
if IsUnicode then
begin
SetLength(Result, BufferLen);
{ Keep D6 from complaining about suspicious PChar cast }
P := @Result[1];
Found := CurrentContextMenu.GetCommandString(MenuItemID-1, GCS_VERBW, nil, PChar(P),
BufferLen) = NOERROR
end;
if not Found then
begin
SetLength(S, BufferLen);
if CurrentContextMenu.GetCommandString(MenuItemID-1, GCS_VERBA, nil, PChar(S),
BufferLen) <> NOERROR
then
Result := ''
else begin
SetLength(S, StrLen( PChar(S)));
Result := S
end
end else
SetLength(Result, lstrlenW(PWideChar( Result)))
end;
end;
function TNamespace.Copy(NamespaceArray: TNamespaceArray): Boolean;
begin
Result := False;
if CanCopyAll(NamespaceArray) then
begin
if VerifyPIDLRelationship(NamespaceArray, True) then
Result := ExecuteContextMenuVerb('copy', NamespaceToRelativePIDLArray(NamespaceArray))
else
ExecuteContextMenuVerbMultiPath('copy', NamespaceArray)
end
end;
constructor TNamespace.Create(PIDL: PItemIdList; AParent: TNamespace);
{ Pass the PIDL of a Namespace Object Folder to create along with its parent }
{ to create a new TNamespace. }
begin
inherited Create;
FParent := AParent;
FShellCache.Data.SmallIcon := -1;
FShellCache.Data.SmallOpenIcon := -1;
FShellCache.Data.OverlayIndex := -1;
FShellCache.Data.OverlayIconIndex := -1;
Include(FStates, nsShellDetailsSupported); // Be optomistic
Include(FStates, nsShellFolder2Supported); // Be optomistic
Include(FStates, nsShellOverlaySupported); // Be optomistic
FreePIDLOnDestroy := True;
FNamespaceID := SHELL_NAMESPACE_ID;
{ It is the Root Folder since it has no parent }
if not Assigned(AParent) then
begin
{ Either a nil for PID or if the PID is the Desktop PIDL means a full tree }
if not Assigned(PIDL) or PIDLMgr.IsDesktopFolder(PIDL) then
begin
{ If PID is already assigned then use it }
if not Assigned(PIDL) then
SHGetSpecialFolderLocation(ParentWnd, CSIDL_DESKTOP, FRelativePIDL)
else
FRelativePIDL := PIDL;
FAbsolutePIDL := FRelativePIDL;
end else
{ The PIDL is the Root PIDL but is NOT the Desktop namespace it is a }
{ FULLY QUALIFIED PIDL to a namespace that is to be the Root. }
begin
FAbsolutePIDL := PIDL;
FRelativePIDL := PIDLMgr.GetPointerToLastID(FAbsolutePIDL);
end;
end else
{ If the folder is a child of the desktop special conditions apply see above }
if PIDLMgr.IsDesktopFolder(AParent.AbsolutePIDL) then
begin
FRelativePIDL := PIDL;
FAbsolutePIDL := PIDL;
end else
{ Normal building of the PIDLs and Shells }
begin
FAbsolutePIDL := PIDLMgr.AppendPIDL(AParent.FAbsolutePIDL, PIDL);
FRelativePIDL := PIDLMgr.GetPointerToLastID(FAbsolutePIDL);
PIDLMgr.FreePIDL(PIDL);
end;
end;
constructor TNamespace.CreateCustomNamespace(CustomID: Integer; AParent: TNamespace);
begin
FShellCache.Data.SmallIcon := -1;
FShellCache.Data.SmallOpenIcon := -1;
Exclude(FStates, nsShellDetailsSupported);
Exclude(FStates, nsShellFolder2Supported);
FreePIDLOnDestroy := False;
FNamespaceID := CustomID;
FParent := AParent
end;
constructor TNamespace.CreateFromFileName(FileName: WideString);
var
PIDL: PItemIDList;
begin
PIDL := PathToPIDL(FileName);
if Assigned(PIDL) then
Create(PIDL, nil)
else
// This will be called often with the autocomplete component while debugging
// in the IDE
// To turn off exception break go to Tools>Debugger Options>Add and type in
// "EVSTInvalidFileName" without the quotes. Make sure that is is checked.
// This will keep delphi from breaking on this exception
raise EVSTInvalidFileName.Create('Trying to create a TNamespace on a non existant File object');
end;
function TNamespace.Cut(NamespaceArray: TNamespaceArray): Boolean;
begin
Result := False;
if CanCutAll(NamespaceArray) then
begin
if VerifyPIDLRelationship(NamespaceArray, True) then
Result := ExecuteContextMenuVerb('cut', NamespaceToRelativePIDLArray(NamespaceArray))
else
ExecuteContextMenuVerbMultiPath('cut', NamespaceArray)
end
end;
function TNamespace.DataObjectMulti(NamespaceArray: TNamespaceArray): IDataObject;
begin
if VerifyPIDLRelationship(NamespaceArray, True) then
Result := InternalGetDataObjectInterface(NamespaceToRelativePIDLArray(NamespaceArray))
else
CreateFullyQualifiedShellDataObject(NamespaceToAbsolutePIDLArray(NamespaceArray), Result);
end;
function TNamespace.Delete(NamespaceArray: TNamespaceArray): Boolean;
begin
Result := False;
if CanDeleteAll(NamespaceArray) then
begin
if VerifyPIDLRelationship(NamespaceArray, True) then
Result := ExecuteContextMenuVerb('delete', NamespaceToRelativePIDLArray(NamespaceArray))
else
ExecuteContextMenuVerbMultiPath('delete', NamespaceArray)
end
end;
destructor TNamespace.Destroy;
begin
// Remember RelativePIDL points to end of AbsolutePIDL so only 1 actual PIDL.
if FreePIDLOnDestroy and Assigned(PIDLMgr) then
PIDLMgr.FreePIDL(FAbsolutePIDL);
if IsUnicode then
begin
if Assigned(FWin32FindDataW) then
FreeMem(FWin32FindDataW, SizeOf(TWin32FindDataW));
end else
if Assigned(FWin32FindDataA) then
FreeMem(FWin32FindDataA, SizeOf(TWin32FindDataA));
begin
end;
if Assigned(FSHGetFileInfoRec) then
begin
Finalize(FSHGetFileInfoRec^);
FreeMem(FSHGetFileInfoRec, SizeOf(TSHGetFileInfoRec));
end;
FreeAndNil(FExtractImage);
FreeAndNil(FShellLink);
FShellFolder := nil;
FreeAndNIL(FImage);
inherited;
if (nsOwnsParent in States) then
FreeAndNil(FParent);
end;
function TNamespace.DetailsAlignment(ColumnIndex: Integer): TAlignment;
{ Returns the Text that is in the Header of the Explorer Listview based on what }
{ the folder in the Treeview is displaying. Only implemented partially on }
{ different versions of Windows. It was undocumented until about Win98. }
{ Win2k implements this using IShellFolder2 }
{ Be careful of the reference point using DetailsXXXX functions. This method }
{ get the header titles a folder will show for its children. }
var
Details: TShellDetails;
Found: Boolean;
begin
// Default
Result := taLeftJustify;
FillChar(Details, SizeOf(Details), #0);
Found := False;
if DetailsValidIndex(ColumnIndex) then
begin
if Assigned(ShellFolder2) then
Found := ShellFolder2.GetDetailsOf(nil, UINT(ColumnIndex), Details) = S_OK;
if not Found and Assigned(ShellDetailsInterface) then
Found := ShellDetailsInterface.GetDetailsOf(nil, UINT(ColumnIndex), Details) = S_OK;
if Found then
begin
case Details.Fmt of
LVCFMT_CENTER: Result := taCenter;
LVCFMT_LEFT: Result := taLeftJustify;
LVCFMT_RIGHT: Result := taRightJustify;
// LVCFMT_COL_HAS_IMAGES: Result := tiContainsImage
end;
if (Details.str.uType = STRRET_WSTR) and Assigned(Details.str.pOleStr) then
PIDLMgr.FreeOLEStr(Details.str.pOLEStr);
end
end
end;
function TNamespace.DetailsColumnTitle(ColumnIndex: integer): WideString;
{ Returns the Text that is in the Header of the Explorer Listview based on what }
{ the folder in the Treeview is displaying. Only implemented partially on }
{ different versions of Windows. It was undocumented until about Win98. }
{ Win2k implements this using IShellFolder2 }
{ Be careful of the reference point using DetailsXXXX functions. This method }
{ gets the header titles a folder will show for its children. }
var
Details: TShellDetails;
Found: Boolean;
begin
FillChar(Details, SizeOf(Details), #0);
Found := False;
if DetailsValidIndex(ColumnIndex) then
begin
if Assigned(ShellFolder2) then
Found := ShellFolder2.GetDetailsOf(nil, UINT(ColumnIndex), Details) = S_OK;
if not Found and Assigned(ShellDetailsInterface) then
Found := ShellDetailsInterface.GetDetailsOf(nil, UINT(ColumnIndex), Details) = S_OK;
if Found then
Result := StrRetToStr(Details.str, RelativePIDL)
else
Result := DetailsDefaultColumnTitle(ColumnIndex)
end else
Result := ''
end;
function TNamespace.DetailsDefaultColumnTitle(ColumnIndex: integer): WideString;
{ If IShellDetails is not implemented then these are returned for the Header }
{ text as a default. Can be overridden. }
begin
case ColumnIndex of
-1, 0: Result := STR_COLUMN_NAMES[0];
1: Result := STR_COLUMN_NAMES[1];
2: Result := STR_COLUMN_NAMES[2];
3: Result := STR_COLUMN_NAMES[3];
4: Result := STR_COLUMN_NAMES[4];
5: Result := STR_COLUMN_NAMES[5];
6: Result := STR_COLUMN_NAMES[6];
7: Result := STR_COLUMN_NAMES[7];
8: Result := STR_COLUMN_NAMES[8];
9: Result := STR_COLUMN_NAMES[9];
end;
end;
function TNamespace.DetailsDefaultOf(ColumnIndex: integer): WideString;
{ If IShellDetail is not implemented the call to DetailsOf calls this and }
{ returns what it can to mimic the values in columns for a plain file, Name, }
{ size, type, date, attributes. }
var
IsSystemFolder: Boolean;
begin
Result := '';
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) then
{ This is totally undocumented. It works on Win98 will test on NT 4 soon }
{ Not a valid file so it has no size. #8 appears to mean "System File" }
IsSystemFolder := not ((FWin32FindDataW^.cFileName[0] = WideChar(#8)) or
(FWin32FindDataW^.cFileName[0] = WideChar(#0)) or
not FileSystem)
else
IsSystemFolder := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) then
{ This is totally undocumented. It works on Win98 will test on NT 4 soon }
{ Not a valid file so it has no size. #8 appears to mean "System File" }
IsSystemFolder := not ((FWin32FindDataA^.cFileName[0] = #8) or
(FWin32FindDataA^.cFileName[0] = #0) or
not FileSystem)
else
IsSystemFolder := False;
end;
case ColumnIndex of
-1, 0: Result := NameInFolder;
1: Result := SizeOfFileKB;
2: if IsSystemFolder then
Result := FileType
else
Result := STR_SYSTEMFOLDER;
3: Result := LastWriteTime;
4: Result := AttributesString;
else
Result := ''
end;
end;
function TNamespace.DetailsDefaultSupportedColumns: integer;
{ If IShellDetail is not implemented the call to SupportedColumns calls this }
{ and returns 5. It mimics the titles in the header for a plain file, Name, }
{ size, type, date, attributes. }
begin
Result := DefaultDetailColumns;
end;
function TNamespace.DetailsGetDefaultColumnState(ColumnIndex: integer): TSHColumnStates;
{ Be careful of the reference point using DetailsXXXX functions. This function }
{ gets the GetDefaultColumnState of the folder for its children if it exposes }
{ IShellFolder2. If it does not it returns csOnByDefault so it will be shown }
var
Flags: Longword;
begin
Result := [];
// Assert(DetailsValidIndex(ColumnIndex), 'Invalid DetailsGetDefaultColumnState in TNamespace.DetailsOf');
if DetailsValidIndex(ColumnIndex) then
begin
EnsureDetailCache;
if (docStatesValid in ShellCache.Data.DetailsOfCache[ColumnIndex].Cached) then
Result := ShellCache.Data.DetailsOfCache[ColumnIndex].States
else begin
if Assigned(ShellFolder2) then
begin
Flags := 0;
if ShellFolder2.GetDefaultColumnState(ColumnIndex, Flags) = NOERROR then
begin
if SHCOLSTATE_TYPE_STR and Flags <> 0 then Include(ShellCache.Data.DetailsOfCache[ColumnIndex].States, csTypeString);
if SHCOLSTATE_TYPE_INT and Flags <> 0 then Include(ShellCache.Data.DetailsOfCache[ColumnIndex].States, csTypeInt);
if SHCOLSTATE_TYPE_DATE and Flags <> 0 then Include(ShellCache.Data.DetailsOfCache[ColumnIndex].States, csTypeDate);
if SHCOLSTATE_ONBYDEFAULT and Flags <> 0 then Include(ShellCache.Data.DetailsOfCache[ColumnIndex].States, csOnByDefault);
if SHCOLSTATE_TYPE_SLOW and Flags <> 0 then Include(ShellCache.Data.DetailsOfCache[ColumnIndex].States, csSlow);
if SHCOLSTATE_EXTENDED and Flags <> 0 then Include(ShellCache.Data.DetailsOfCache[ColumnIndex].States, csExtended);
if SHCOLSTATE_SECONDARYUI and Flags <> 0 then Include(ShellCache.Data.DetailsOfCache[ColumnIndex].States, csSecondaryUI);
if SHCOLSTATE_HIDDEN and Flags <> 0 then Include(ShellCache.Data.DetailsOfCache[ColumnIndex].States, csHidden);
end else
{ Some of the old namespaces will expose ShellFolder2 but don't support }
{ it completely. These resort to IShellDetails so assume this is the case }
ShellCache.Data.DetailsOfCache[ColumnIndex].States := [csOnByDefault];
end else
{ Some of the old namespaces don't expose ShellFolder2. These resort to }
{ IShellDetails so assume this is the case }
ShellCache.Data.DetailsOfCache[ColumnIndex].States := [csOnByDefault];
Result := ShellCache.Data.DetailsOfCache[ColumnIndex].States;
Include(ShellCache.Data.DetailsOfCache[ColumnIndex].Cached, docStatesValid)
end
end
end;
function TNamespace.DetailsOf(ColumnIndex: integer): WideString;
{ Returns the text for the desired column (detail view in the listview in }
{ Explorer) using IShellDetail or using information pulled from the namespace }
{ by other means. }
{ Be careful of the reference point using DetailsXXXX functions. This function }
{ gets the Details of the current namespace using its parent folder. }
// Threading only works on Namespaces that support IShellFolder2 (WinME, Win2k and up)
var
Details: TShellDetails;
OldError: Integer;
TempCache: PDetailsOfCacheRec;
begin
Result := '';
if DetailsValidIndex(ColumnIndex) then
begin
OldError := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
try
{ Force parent namespace creation if necessary }
if Assigned(ParentShellFolder) and not IsDesktop then
begin
{ The parent is responsible for the columns }
if DetailsValidIndex(ColumnIndex) then
begin
EnsureDetailCache;
if (docCaptionValid in ShellCache.Data.DetailsOfCache[ColumnIndex].Cached) then
Result := ShellCache.Data.DetailsOfCache[ColumnIndex].Caption
else begin
FillChar(Details, SizeOf(Details), #0);
TempCache := @ShellCache.Data.DetailsOfCache[ColumnIndex];
if ColumnIndex = 0 then
TempCache^.Caption := NameInFolder
else
if Assigned(ParentShellFolder2) then
begin
if ParentShellFolder2.GetDetailsOf(RelativePIDL, UINT(ColumnIndex), Details) <> S_OK then
begin
if Assigned(ParentShellDetailsInterface) and (ParentShellDetailsInterface.GetDetailsOf(RelativePIDL, UINT(ColumnIndex), Details) = S_OK) then
TempCache^.Caption := StrRetToStr(Details.Str, RelativePIDL)
else
TempCache^.Caption := DetailsDefaultOf(ColumnIndex)
end else
TempCache^.Caption := StrRetToStr(Details.Str, RelativePIDL);
Include(TempCache^.Cached, docCaptionValid)
end else
begin
if Assigned(ParentShellDetailsInterface) then
begin
if ParentShellDetailsInterface.GetDetailsOf(RelativePIDL, UINT(ColumnIndex), Details) = S_OK then
TempCache^.Caption := StrRetToStr(Details.Str, RelativePIDL);
end else
TempCache^.Caption := DetailsDefaultOf(ColumnIndex);
Include(TempCache^.Cached, docCaptionValid);
end;
Result := TempCache^.Caption;
end
end
end
finally
SetErrorMode(OldError);
end
end
end;
function TNamespace.DetailsOfEx(ColumnIndex: integer): WideString;
var
ColumnID: TSHColumnID;
V: OLEVariant;
ColState: TSHColumnStates;
// Date: TDateTime;
begin
Result := '';
V := Null;
if Assigned(ParentShellFolder2) then
begin
ColState := Parent.DetailsGetDefaultColumnState(ColumnIndex);
FillChar(ColumnID, SizeOf(ColumnID), #0);
if ParentShellFolder2.MapColumnToSCID(ColumnIndex, ColumnID) = NOERROR then
if ParentShellFolder2.GetDetailsEx(RelativePIDL, ColumnID, V) = NOERROR then
begin
if csTypeString in ColState then
Result := WideString(V)
else
if csTypeInt in ColState then
Result := IntToStr( Integer(V))
else
// if csTypeDate in ColState then
// Date := V;
end else
Result := DetailsDefaultOf(ColumnIndex)
else
Result := DetailsDefaultOf(ColumnIndex)
end
end;
function TNamespace.DetailsSupportedColumns: integer;
{ If IShellDetail or IShellFolder2 is implemented the call to }
{ DetailsSupportedColumns returns total number of columns the namespace }
{ supports. This allows the header to change dynamiclly. }
{ Be careful of the reference point using DetailsXXXX functions. This function }
{ gets number of columns this folder will display for its children. }
const
{ ShellFolder2 is broken on WinME for "Scanners and Cameras" folders. }
{ It goes into an infinate loop. }
{ WinXP is just as broken. }
COLUMNLIMIT = 1000; // Safely valve for namespaces that don't follow the rules
var
Details: TShellDetails;
Flags: DWord;
Found: Boolean;
begin
FillChar(Details, SizeOf(Details), #0);
if not (scSupportedColumns in ShellCache.ShellCacheFlags) then
begin
FShellCache.Data.SupportedColumns := 0;
if Assigned(ShellFolder2) then
begin
while (ShellFolder2.GetDefaultColumnState(FShellCache.Data.SupportedColumns, Flags) = NOERROR) and
(FShellCache.Data.SupportedColumns < COLUMNLIMIT) do
Inc(FShellCache.Data.SupportedColumns);
// Error detected, the namespace does not follow the rules
if FShellCache.Data.SupportedColumns = COLUMNLIMIT then
FShellCache.Data.SupportedColumns := 0;
Found := FShellCache.Data.SupportedColumns > 0;
{ Some folders support both methods but only work right with GetDetailsOf }
if not Found then
while (ShellFolder2.GetDetailsOf(nil, UINT(ShellCache.Data.SupportedColumns), Details) = S_OK) and
(FShellCache.Data.SupportedColumns < COLUMNLIMIT) do
begin
Inc(FShellCache.Data.SupportedColumns);
if (Details.str.uType = STRRET_WSTR) and Assigned(Details.str.pOleStr) then
PIDLMgr.FreeOLEStr(Details.str.pOLEStr);
end;
// Error detected, the namespace does not follow the rules
if FShellCache.Data.SupportedColumns = COLUMNLIMIT then
FShellCache.Data.SupportedColumns := 0;
end;
{ Some folders support both but only work right with IShellDetials }
{ The History Folder is an example. }
Found := FShellCache.Data.SupportedColumns > 0;
{ DO NOT PASS A FREAKING UNInitialIZED TSHELLDETAIL STRUCTURE TO THIS FUNCTION }
{ IT WILL CAUSE THE RESULT TO BE CORRECT BUT INTERLACED WITH GARBAGE. }
if not Found and Assigned(ShellDetailsInterface) then
while ShellDetailsInterface.GetDetailsOf(nil, UINT(ShellCache.Data.SupportedColumns), Details) = S_OK do
Inc(FShellCache.Data.SupportedColumns);
if ShellCache.Data.SupportedColumns = 0 then
FShellCache.Data.SupportedColumns := DetailsDefaultSupportedColumns;
Include(FShellCache.ShellCacheFlags, scSupportedColumns);
end;
Result := ShellCache.Data.SupportedColumns
end;
function TNamespace.DetailsSupportedVisibleColumns: TVisibleColumnIndexArray;
// Returns and array of currently visible columns in details mode. Two bits of info
// are returned with this method.
// 1) The number of visible column: Length(DetailsSupportedVisibleColumns)
// 2) The indicies of visible columns: [0, 2, 4, 6] Details index 0, 2, 4, 6 are shown\
var
i: integer;
begin
Result := nil;
for i := 0 to DetailsSupportedColumns - 1 do
begin
if csOnByDefault in DetailsGetDefaultColumnState(i) then
begin
if DetailsColumnTitle(i) <> '' then
begin
SetLength(Result, Length(Result) + 1);
Result[Length(Result) - 1] := i
end
end
end;
end;
function TNamespace.DetailsValidIndex(DetailsIndex: integer): Boolean;
{ Test to see if the passed index is in the range of the number of detail }
{ columns the namespace has. }
begin
Result := (DetailsIndex > -1) and (DetailsIndex < DetailsSupportedColumns)
end;
function TNamespace.DragEffect(grfKeyState: integer): HRESULT;
{ Looks at the KeyState during a IDragDrop notification. The return value }
{ is the Effect that is desired by the user, using the GetDesiredDragEffect}
{ function, and what Effects are supported by the IDragSource }
function AvailableEffects: LongInt;
begin
Result := DROPEFFECT_NONE;
if CanMove then Result := DROPEFFECT_MOVE;
if CanCopy then Result := Result or DROPEFFECT_COPY;
if CanLink then Result := Result or DROPEFFECT_LINK;
end;
var
KeyEffect: HResult;
ValidEffects: Longword;
begin
// See what the user is requesting by looking at the key board
KeyEffect := RequestedDragEffect(grfKeyState);
// What effects do the namespace support?
ValidEffects := AvailableEffects;
// Let the users desires prevail
if KeyEffect and ValidEffects > 0 then Result := KeyEffect
else // If the users desires are undo-able pick the first effect avaiable
if ValidEffects and DROPEFFECT_MOVE > 1 then Result := DROPEFFECT_MOVE
else // Windows default is MOVE so check it first
if ValidEffects and DROPEFFECT_COPY > 1 then Result := DROPEFFECT_COPY
else
if ValidEffects and DROPEFFECT_LINK > 1 then Result := DROPEFFECT_LINK
else
Result := DROPEFFECT_NONE;
end;
function TNamespace.DisplayNameOf(Flags: Longword): WideString;
var
StrRet: TSTRRET;
begin
if Assigned(ParentShellFolder) then
begin
FillChar(StrRet, SizeOf(StrRet), #0);
if ParentShellFolder.GetDisplayNameOf(RelativePIDL, Flags, StrRet) = NOERROR
then
Result := StrRetToStr(StrRet, RelativePIDL)
else
Result := '';
end else
Result := ''
end;
function TNamespace.DragEnter(const dataObj: IDataObject;
grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult;
{ Called when there is a pending COM drop on the namespace. The namespace will }
{ decide if it can handle the information passed. }
begin
if DropTarget and Assigned(DropTargetInterface) then
Result := DropTargetInterface.DragEnter(dataObj, grfKeyState, pt, dwEffect)
else begin
dwEffect := DROPEFFECT_NONE;
Result := S_OK
end
end;
function TNamespace.DragLeave: HResult;
{ Called when there is a pending COM drop on the namespace. The namespace will }
{ decide if it can handle the information passed. }
begin
if DropTarget and Assigned(DropTargetInterface) then
Result := DropTargetInterface.DragLeave
else
Result := S_OK
end;
function TNamespace.DragOver(grfKeyState: Integer; pt: TPoint;
var dwEffect: Integer): HResult;
{ Called when there is a pending COM drop on the namespace. The namespace will }
{ decide if it can handle the information passed. }
begin
if DropTarget and Assigned(DropTargetInterface) then
Result := DropTargetInterface.DragOver(grfKeyState, pt, dwEffect)
else begin
dwEffect := DROPEFFECT_NONE;
Result := S_OK
end
end;
function TNamespace.Drop(const dataObj: IDataObject; grfKeyState: Integer;
pt: TPoint; var dwEffect: Integer): HResult;
{ Called when there is a COM object is dropped on the namespace. The namespace }
{ will handle the action as well. }
begin
if DropTarget and Assigned(DropTargetInterface) then
Result := DropTargetInterface.Drop(dataObj, grfKeyState, pt, dwEffect)
else begin
dwEffect := DROPEFFECT_NONE;
Result := S_OK
end
end;
function TNamespace.EnumerateFolder(MessageWnd: HWnd; Folders, NonFolders,
IncludeHidden: Boolean; EnumFunc: TEnumFolderCallback;
UserData: Pointer): integer;
{ Enumerate a folder to get its subfolders. For each subfolder the the }
{ callback function is called so a new TNamespace may be created. }
{ You have a choice to receive Folders, NonFolders (files), and Hidden }
{ objects, UserData is useful to pass info back to the callback function. }
{ Encapsulates the IShellFolder.EnumObjects function }
{ The reciever of the Callback function is responsible for Freeing the PIDLs! }
{ Returns the number of objects in the folder. }
var
Enum: IEnumIDList;
Flags: Longword;
Fetched: Longword;
Item: PItemIDList;
Terminate: Boolean;
OldError: integer;
begin
Result := 0;
{ This fixed a problem Rik Baker had: }
{ "The error message is "C:\WINDOWS\SYSTEM\ODBCINST.DLL is not a valid }
{ Windows Image", however the file appears fine and I've now seen the same }
{ message on 9 different 2000 boxes spread across the country. }
OldError := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
try
if Assigned(ShellFolder) then
begin
if Assigned(EnumFunc) then
begin
Terminate := False;
Flags := 0;
if Folders then
Flags := Flags or SHCONTF_FOLDERS;
if NonFolders then
Flags := Flags or SHCONTF_NONFOLDERS;
if IncludeHidden then
Flags := Flags or SHCONTF_INCLUDEHIDDEN;
if Valid then
begin
// Right now you can't mix custom items and real shell items in the same folder
if ShellFolder.EnumObjects(MessageWnd, Flags, Enum) = NOERROR then
begin
// Vista Enum is nil every once in a while
if Assigned(Enum) then
begin
while (Enum.Next(1, Item, Fetched) = NOERROR) and not Terminate do
begin
if EnumFunc(MessageWnd, Item, Self, UserData, Terminate) then
Inc(Result)
end
end
end
end
end
end
finally
SetErrorMode(OldError);
end
end;
function TNamespace.EnumerateFolderEx(MessageWnd: HWnd; FileObjects: TFileObjects;
EnumFunc: TEnumFolderCallback; UserData: pointer; AfterValidEnumIDList: TNotifyEvent = nil): integer;
{ Enumerate a folder to get its subfolders. For each subfolder the the }
{ callback function is called so a new TNamespace may be created. }
{ You have a choice to receive Folders, NonFolders (files), and Hidden }
{ objects, UserData is useful to pass info back to the callback function. }
{ Encapsulates the IShellFolder.EnumObjects function }
{ The reciever of the Callback function is responsible for Freeing the PIDLs! }
{ Returns the number of objects in the folder. }
var
Enum: IEnumIDList;
Flags: Longword;
Fetched: Longword;
Item: PItemIDList;
Terminate: Boolean;
OldError: integer;
begin
Result := 0;
{ This fixed a problem Rik Baker had: }
{ "The error message is "C:\WINDOWS\SYSTEM\ODBCINST.DLL is not a valid }
{ Windows Image", however the file appears fine and I've now seen the same }
{ message on 9 different 2000 boxes spread across the country. }
OldError := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
try
if not MP_UseModalDialogs then
MessageWnd := 0;
if Assigned(ShellFolder) then
begin
Flags := FileObjectsToFlags(FileObjects);
Terminate := False;
if Valid then
begin
// Right now you can't mix custom items and real shell items in the same folder
if ShellFolder.EnumObjects(MessageWnd, Flags, Enum) = NOERROR then
begin
// Vista Enum is nil every once in a while
if Assigned(Enum) then
begin
if Assigned(AfterValidEnumIDList) then
AfterValidEnumIDList(Self);
// Allows calling with a nil EnumFunc to pop messages in the EnumObjects call above
if not Assigned(EnumFunc) then
EnumFunc := EnumFuncDummy;
if Assigned(Enum) then
while (Enum.Next(1, Item, Fetched) = NOERROR) and not Terminate do
begin
if EnumFunc(MessageWnd, Item, Self, UserData, Terminate) then
Inc(Result)
end
end
end
end
end
finally
SetErrorMode(OldError);
end
end;
function TNamespace.ExecuteContextMenuVerb(AVerb: WideString;
APIDLArray: TRelativePIDLArray; MessageWindowParent: HWnd = 0): Boolean;
const
MaxVerbLen = 128;
var
ContextMenu, ContextMenu2: IContextMenu;
Menu: hMenu;
InvokeInfo: TCMInvokeCommandInfoEx;
i: integer;
VerbA, AVerbA: string;
VerbW: WideString;
VerbFound, StrFound: Boolean;
MenuID: LongWord;
GenericVerb: Pointer;
begin
if Assigned(ParentShellFolder) then
begin
if Assigned(APIDLArray) then
ContextMenu := InternalGetContextMenuInterface(APIDLArray)
else
ContextMenu := ContextMenuInterface;
if Assigned(ContextMenu) then
ContextMenu.QueryInterface(IID_IContextMenu2, ContextMenu2);
Menu := CreatePopupMenu;
if Assigned(ContextMenu) or Assigned(ContextMenu2) then
begin
try
if Assigned(ContextMenu2) then
ContextMenu2.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_NORMAL or CMF_EXPLORE or CMF_DEFAULTONLY)
else
ContextMenu.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_NORMAL or CMF_EXPLORE or CMF_DEFAULTONLY);
FillChar(InvokeInfo, SizeOf(InvokeInfo), #0);
if IsUnicode then
begin
SetLength(VerbW, MaxVerbLen);
GenericVerb := @VerbW[1];
end
else begin
SetLength(VerbA, MaxVerbLen);
GenericVerb := @VerbA[1];;
AVerbA := AVerb
end;
VerbFound := False;
i := 0;
{ The result of using the 'verb' string and the MakeIntResource is }
{ different expecially on system folders. This forces it to use }
{ MakeIntResource if it can. }
while (i < GetMenuItemCount(Menu)) and not VerbFound do
begin
MenuID := GetMenuItemID(Menu, i);
if (MenuID <> $FFFFFFFF) and (MenuID > 0) then
begin
FillChar(GenericVerb^, Length(VerbW) * 2, #0);
if IsUnicode then
begin
if Assigned(ContextMenu2) then
StrFound := Succeeded(ContextMenu2.GetCommandString(MenuID-1, GCS_VERBW, nil, GenericVerb, MaxVerbLen))
else
StrFound := Succeeded(ContextMenu.GetCommandString(MenuID-1, GCS_VERBW, nil, GenericVerb, MaxVerbLen));
if StrFound then
begin
SetLength(VerbW, lstrlenW(PWideChar( VerbW)));
if lstrcmpiW_MP(PWideChar(VerbW), PWideChar(AVerb)) = 0 then
begin
InvokeInfo.fMask := CMIC_MASK_UNICODE;
{ For some reason the lpVerbW won't work }
InvokeInfo.lpVerb := MakeIntResourceA(MenuID-1);
InvokeInfo.lpVerbW := MakeIntResourceW(MenuID-1);
VerbFound := True
end;
SetLength(VerbW, MaxVerbLen);
end
end else
begin
if Assigned(ContextMenu2) then
StrFound := Succeeded(ContextMenu2.GetCommandString(MenuID-1, GCS_VERB, nil, GenericVerb, MaxVerbLen))
else
StrFound := Succeeded(ContextMenu.GetCommandString(MenuID-1, GCS_VERB, nil, GenericVerb, MaxVerbLen));
if StrFound then
begin
SetLength(VerbA, StrLen(PChar( VerbA)));
if lstrcmpi(PChar( VerbA), PChar(AVerbA)) = 0 then
begin
InvokeInfo.lpVerb := MakeIntResourceA(MenuID-1);
VerbFound := True
end;
SetLength(VerbA, MaxVerbLen);
end
end
end;
Inc(i)
end;
if not VerbFound then
begin
if IsUnicode then
begin
InvokeInfo.fMask := CMIC_MASK_UNICODE;
InvokeInfo.lpVerbW := PWideChar( AVerb);
InvokeInfo.lpVerb := PChar( string( AVerb))
end else
InvokeInfo.lpVerb := PChar( AVerbA);
end;
if IsUnicode then
InvokeInfo.cbSize := SizeOf(TCMInvokeCommandInfoEx)
else
InvokeInfo.cbSize := SizeOf(TCMInvokeCommandInfo);
if MessageWindowParent = 0 then
InvokeInfo.hWnd := ParentWnd
else
InvokeInfo.hWnd := MessageWindowParent;
InvokeInfo.nShow := SW_SHOWNORMAL;
if Assigned(ContextMenu2) then
Result := Succeeded(ContextMenu2.InvokeCommand(InvokeInfo))
else
Result := Succeeded(ContextMenu.InvokeCommand(InvokeInfo))
finally
if Menu <> 0 then
DestroyMenu(Menu);
end;
end else
Result := False
end else
Result := False
end;
function TNamespace.FolderSize(Invalidate: Boolean; RecurseFolder: Boolean = False): Int64;
begin
if not(scFolderSize in ShellCache.ShellCacheFlags) or Invalidate then
begin
if Folder and FileSystem then
FShellCache.Data.FolderSize := CalcuateFolderSize(NameForParsing, RecurseFolder);
Include(FShellCache.ShellCacheFlags, scFolderSize);
end;
Result := FShellCache.Data.FolderSize
end;
function TNamespace.ExplorerStyleAttributeStringList(CapitalLetters: Boolean): WideString;
begin
Result := '';
if Archive then
Result := Result + STR_ARCHIVE;
if Hidden then
Result := Result + STR_HIDDEN;
if ReadOnlyFile then
Result := Result + STR_READONLY;
if SystemFile then
Result := Result + STR_SYSTEM;
if Compressed then
Result := Result + STR_COMPRESS;
if not CapitalLetters then
Result := WideLowerCase(Result)
end;
function TNamespace.GetArchive: Boolean;
{ GETTER: Does the file attributes contain Archive? }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_ARCHIVE <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_ARCHIVE <> 0
else
Result := False;
end
end;
function TNamespace.GetAttributesString: WideString;
begin
if FileSystem then
Result := ExplorerStyleAttributeStringList(True)
else
Result := ''
end;
function TNamespace.GetBrowsable: Boolean;
begin
if not (scBrowsable in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_BROWSABLE, False) then
Include(FShellCache.Data.Attributes, caBrowsable);
Include(FShellCache.ShellCacheFlags, scBrowsable);
end;
Result := caBrowsable in ShellCache.Data.Attributes
end;
function TNamespace.GetCanCopy: Boolean;
begin
if not (scCanCopy in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_CANCOPY, False) then
Include(FShellCache.Data.Attributes, caCanCopy);
Include(FShellCache.ShellCacheFlags, scCanCopy);
end;
Result := caCanCopy in ShellCache.Data.Attributes
end;
function TNamespace.GetCanDelete: Boolean;
{ GETTER: Can we delete the namespace? }
begin
if not (scCanDelete in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_CANDELETE, False) then
Include(FShellCache.Data.Attributes, caCanDelete);
Include(FShellCache.ShellCacheFlags, scCanDelete);
end;
Result := caCanDelete in ShellCache.Data.Attributes
end;
function TNamespace.GetCanLink: Boolean;
begin
if not (scCanLink in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_CANLINK, False) then
Include(FShellCache.Data.Attributes, caCanLink);
Include(FShellCache.ShellCacheFlags, scCanLink);
end;
Result := caCanLink in ShellCache.Data.Attributes
end;
function TNamespace.GetCanMove: Boolean;
begin
if not (scCanMove in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_CANMOVE, False) then
Include(FShellCache.Data.Attributes, caCanMove);
Include(FShellCache.ShellCacheFlags, scCanMove);
end;
Result := caCanMove in ShellCache.Data.Attributes
end;
function TNamespace.GetCanRename: Boolean;
{ GETTER: Can we Rename the namespace? }
begin
if not (scCanRename in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_CANRENAME, False) then
Include(FShellCache.Data.Attributes, caCanRename);
Include(FShellCache.ShellCacheFlags, scCanRename);
end;
Result := caCanRename in ShellCache.Data.Attributes
end;
function TNamespace.GetCategoryCount: Integer;
begin
LoadCategoryInfo;
Result := 0;
end;
function TNamespace.GetCLSID: TGUID;
var
DescriptionID: TSHDESCRIPTIONID;
PersistFolder: IPersistFolder;
begin
Result := GUID_NULL;
if Assigned(ParentShellFolder) then
if Succeeded(SHGetDataFromIDList(ParentShellFolder, RelativePIDL, SHGDFIL_DESCRIPTIONID, @DescriptionID, SizeOf(TSHDESCRIPTIONID))) then
Result := DescriptionID.Id;
if IsEqualGUID(Result, GUID_NULL) then
begin
if Succeeded(ShellFolder.QueryInterface(IPersistFolder, PersistFolder)) then
if not Succeeded(PersistFolder.GetClassID(Result)) then
Result := GUID_NULL;
end;
end;
function TNamespace.GetCompressed: Boolean;
{ GETTER: Does the file attributes contain Compressed? }
begin
if not (scCompressed in ShellCache.ShellCacheFlags) then
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
if FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_COMPRESSED <> 0 then
Include(FShellCache.Data.Attributes, caCompressed)
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
if FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_COMPRESSED <> 0 then
Include(FShellCache.Data.Attributes, caCompressed)
end;
Include(FShellCache.ShellCacheFlags, scCompressed);
end;
Result := caCompressed in ShellCache.Data.Attributes;
end;
function TNamespace.GetContextMenuInterface: IContextMenu;
var
PIDLArray: TRelativePIDLArray;
begin
if not Assigned(Result) then
begin
SetLength(PIDLArray, 1);
PIDLArray[0] := RelativePIDL;
Result := InternalGetContextMenuInterface(PIDLArray);
end
end;
function TNamespace.GetContextMenu2Interface: IContextMenu2;
var
Found: Boolean;
ContextMenu: IContextmenu;
begin
Found := False;
ContextMenu := ContextMenuInterface;
if Assigned(ContextMenu) then
begin
Found := ContextMenu.QueryInterface(IID_IContextMenu2, Pointer(Result)) <> E_NOINTERFACE;
CurrentContextMenu2 := Result
end;
if not Found then
Result := nil
end;
function TNamespace.GetContextMenu3Interface: IContextMenu3;
var
Found: Boolean;
ContextMenu: IContextmenu;
begin
Found := False;
ContextMenu := ContextMenuInterface;
if Assigned(ContextMenu) then
begin
Found := ContextMenu.QueryInterface(IContextMenu3, Pointer(Result)) <> E_NOINTERFACE;
CurrentContextMenu2 := Result
end;
if not Found then
Result := nil
end;
function TNamespace.GetCreationTime: WideString;
{ GETTER: Creation time of the file. }
begin
if not (scCreationTime in ShellCache.ShellCacheFlags) then
begin
{ Don't use Win32FindData cache, re-read the file times }
GetFileTimes;
if IsUnicode then
begin
if Assigned(FWin32FindDataW) and FileSystem then
FShellCache.Data.CreationTime := ConvertTFileTimeToLocalStr(FWin32FindDataW^.ftCreationTime)
else
FShellCache.Data.CreationTime := '';
end else
begin
if Assigned(FWin32FindDataA) and FileSystem then
FShellCache.Data.CreationTime := ConvertTFileTimeToLocalStr(FWin32FindDataA^.ftCreationTime)
else
FShellCache.Data.CreationTime := '';
end;
Include(FShellCache.ShellCacheFlags, scCreationTime);
end;
Result := ShellCache.Data.CreationTime
end;
function TNamespace.GetCreationDateTime: TDateTime;
begin
Result := ConvertFileTimetoDateTime(CreationTimeRaw)
end;
function TNamespace.GetCreationTimeRaw: TFileTime;
begin
{ Don't use Win32FindData cache, re-read the file times }
GetFileTimes;
if IsUnicode then
begin
if Assigned(FWin32FindDataW) then
Result := FWin32FindDataW^.ftCreationTime
else
FillChar(Result, SizeOf(Result), #0);
end else
begin
if Assigned(FWin32FindDataA) then
Result := FWin32FindDataA^.ftCreationTime
else
FillChar(Result, SizeOf(Result), #0);
end
end;
function TNamespace.GetParent: TNamespace;
var
P: PItemIDList;
begin
if not Assigned(FParent) then
begin
P := PIDLMgr.CopyPIDL(AbsolutePIDL);
if PIDLMgr.IDCount(P) > 1 then
FParent := TNamespace.Create(PIDLMgr.StripLastID(P), nil)
else
FParent := TNamespace.Create(nil, nil);
Include(FStates, nsOwnsParent);
end;
Result := FParent;
end;
function TNamespace.GetThreadedDetailLoaded(ColumnIndex: Integer): Boolean;
begin
Result := False;
EnsureDetailCache;
if DetailsValidIndex(ColumnIndex) then
Result := docThreadLoaded in ShellCache.Data.DetailsOfCache[ColumnIndex].Cached
end;
function TNamespace.GetThreadedDetailLoading(ColumnIndex: Integer): Boolean;
begin
Result := False;
EnsureDetailCache;
if DetailsValidIndex(ColumnIndex) then
Result := docThreadLoading in ShellCache.Data.DetailsOfCache[ColumnIndex].Cached
end;
function TNamespace.ParentWnd: HWnd;
begin
Result := 0;
if MP_UseModalDialogs then
Result := GetActiveWindow
end;
procedure TNamespace.EnsureDetailCache;
var
i: Integer;
TempCache: PDetailsOfCacheRec;
begin
if not (scDetailsOfCache in ShellCache.ShellCacheFlags) then
begin
SetLength(FShellCache.Data.DetailsOfCache, DetailsSupportedColumns);
for i := 0 to Length(FShellCache.Data.DetailsOfCache) - 1 do
begin
TempCache := @ShellCache.Data.DetailsOfCache[i];
TempCache^.Cached := [];
TempCache^.Caption := '';
TempCache^.States := [];
end;
Include(FShellCache.ShellCacheFlags, scDetailsOfCache);
end;
end;
procedure TNamespace.ExecuteContextMenuVerbMultiPath(Verb: WideString;
Namespaces: TNamespaceArray);
var
Menu: TCommonShellMultiParentContextMenu;
Temp: TWinControl;
begin
Menu := TCommonShellMultiParentContextMenu.Create(nil);
Temp := TWinControl.CreateParented(GetDesktopWindow);
try
Temp.Width := 0;
Temp.Height := 0;
Temp.Visible := True;
Menu.ExecuteContextMenuVerb(Temp, Namespaces, Verb);
Temp.Visible := False;
finally
Temp.Free;
Menu.Free
end
end;
procedure TNamespace.GetDataFromIDList;
{ Retrieves and caches the Data stored by the shell PIDL. }
var
Error: Boolean;
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) and not IsDesktop then
begin
if not (scInvalidIDListData in ShellCache.ShellCacheFlags) then
begin
Error := True;
try
if Assigned(ParentShellFolder) then
begin
GetMem(FWin32FindDataW, SizeOf(TWin32FindDataW));
FillChar(FWin32FindDataW^, SizeOf(FWin32FindDataW^), #0);
{ Children of the Desktop won't work if accessed from the Desktop }
{ ShellFolder, they must use the physical Desktop folder. }
if Assigned(Parent) and (Parent.IsDesktop) and Assigned(PhysicalDesktopFolder) then
begin
Error := SHGetDataFromIDListW_MP(PhysicalDesktopFolder.ShellFolder, RelativePIDL,
SHGDFIL_FINDDATA, FWin32FindDataW, SizeOf(TWin32FindDataW)) <> NOERROR;
end else
Error := SHGetDataFromIDListW_MP(ParentShellFolder, RelativePIDL, SHGDFIL_FINDDATA,
FWin32FindDataW, SizeOf(TWin32FindDataW)) <> NOERROR;
end
finally
if Error then
begin
if Assigned(FWin32FindDataW) then
FreeMem(FWin32FindDataW, SizeOf(TWin32FindDataW));
FWin32FindDataW := nil;
Include(FShellCache.ShellCacheFlags, scInvalidIDListData)
end
end;
end
end
end else
begin
if not Assigned(FWin32FindDataA) and not IsDesktop then
begin
if not (scInvalidIDListData in ShellCache.ShellCacheFlags) then
begin
Error := True;
try
if Assigned(ParentShellFolder) then
begin
GetMem(FWin32FindDataA, SizeOf(TWin32FindDataA));
FillChar(FWin32FindDataA^, SizeOf(TWin32FindDataA), #0);
{ Children of the Desktop won't work if accessed from the Desktop }
{ ShellFolder, they must use the physical Desktop folder. }
if Assigned(Parent) and (Parent.IsDesktop) and Assigned(PhysicalDesktopFolder) then
begin
Error := SHGetDataFromIDListA(PhysicalDesktopFolder.ShellFolder, RelativePIDL,
SHGDFIL_FINDDATA, FWin32FindDataA, SizeOf(TWin32FindDataA)) <> NOERROR;
end else
Error := SHGetDataFromIDListA(ParentShellFolder, RelativePIDL, SHGDFIL_FINDDATA,
FWin32FindDataA, SizeOf(TWin32FindDataA)) <> NOERROR;
end
finally
if Error then
begin
if Assigned(FWin32FindDataA) then
FreeMem(FWin32FindDataA, SizeOf(TWin32FindDataA));
FWin32FindDataA := nil;
Include(FShellCache.ShellCacheFlags, scInvalidIDListData)
end
end;
end
end
end
end;
function TNamespace.GetDataObjectInterface: IDataObject;
begin
Result := InternalGetDataObjectInterface(nil)
end;
function TNamespace.GetDescription: TObjectDescription;
var
DescriptionID: TSHDESCRIPTIONID;
begin
Result := odError;
if Assigned(ParentShellFolder) then
begin
if Succeeded(SHGetDataFromIDList(ParentShellFolder, RelativePIDL, SHGDFIL_DESCRIPTIONID, @DescriptionID, SizeOf(TSHDESCRIPTIONID))) then
Result := TObjectDescription(DescriptionID.dwDescriptionId)
end
end;
function TNamespace.GetDetailsSupported: Boolean;
begin
{ IShellDetails depends on the parent folder implementing the interface }
if Assigned(Parent) then
Result := Assigned(Parent.ShellFolder2) or Assigned(ParentShellDetailsInterface)
else
Result := False
end;
function TNamespace.GetDirectory: Boolean;
{ GETTER: Does the file attributes contain Directory? }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0
else
Result := False;
end
end;
function TNamespace.GetDropTarget: Boolean;
{ GETTER: Can we drop another object on this namespace? Note the Desktop is }
{ handled as a special case. The IDropTarget is mapped to the physical folder }
{ location in the DropTargetInterface property. }
begin
Result := TestAttributesOf(SFGAO_DROPTARGET, False) or
PIDLMgr.IsDesktopFolder(RelativePIDL);
end;
function TNamespace.GetDropTargetInterface: IDropTarget;
var
Found: Boolean;
begin
if not Assigned(FDropTargetInterface) then
begin
Found := False;
if Assigned(ParentShellFolder) then
begin
Found := ParentShellFolder.GetUIObjectOf(ParentWnd, 1, FRelativePIDL,
IID_IDropTarget, nil, Pointer(FDropTargetInterface)) = NOERROR;
end;
if not Found and IsDesktop then
FDropTargetInterface := PhysicalDesktopFolder.DropTargetInterface;
end;
Result := FDropTargetInterface
end;
function TNamespace.GetExtension: WideString;
begin
Result := WideExtractFileExt(NameForParsingInFolder);
end;
function TNamespace.GetExtractImage: TExtractImage;
begin
if not Assigned(FExtractImage) then
begin
FExtractImage := TExtractImage.Create;
FExtractImage.Owner := Self
end;
Result := FExtractImage
end;
function TNamespace.GetExtractIconAInterface: IExtractIcon;
var
Found: Boolean;
begin
if Assigned(ParentShellFolder) then
begin
Found := Succeeded(ParentShellFolder.GetUIObjectOf(ParentWnd, 1, FRelativePIDL, IExtractIconA, nil, Pointer(Result)));
if not Found and Assigned(ShellFolder) then
Found := Succeeded(ShellFolder.CreateViewObject(ParentWnd, IExtractIconA, Pointer(Result)));
if not Found then
Result := nil
end
end;
function TNamespace.GetExtractIconWInterface: IExtractIconW;
var
Found: Boolean;
begin
if Assigned(ParentShellFolder) then
begin
Found := Succeeded(ParentShellFolder.GetUIObjectOf(ParentWnd, 1, FRelativePIDL, IExtractIconW, nil, Pointer(Result)));
if not Found and Assigned(ShellFolder) then
Found := Succeeded(ShellFolder.CreateViewObject(ParentWnd, IExtractIconW, Pointer(Result)));
if not Found then
Result := nil
end
end;
function TNamespace.GetFileName: WideString;
{ GETTER: FileName from the file system (FindFirst) }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.cFileName
else
Result := '';
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.cFileName
else
Result := '';
end
end;
function TNamespace.GetFileSysAncestor: Boolean;
// Only works reliablely on Win2k and above
begin
if not (scFileSysAncestor in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_FILESYSANCESTOR, False) then
Include(FShellCache.Data.Attributes, caFileSysAncestor);
Include(FShellCache.ShellCacheFlags, scFileSysAncestor);
end;
Result := caFileSysAncestor in ShellCache.Data.Attributes
end;
function TNamespace.GetFileSystem: Boolean;
{ GETTER: Is the namespace part of the physical file system? }
begin
if not (scFileSystem in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_FILESYSTEM, False) then
Include(FShellCache.Data.Attributes, caFileSystem);
Include(FShellCache.ShellCacheFlags, scFileSystem);
end;
Result := caFileSystem in ShellCache.Data.Attributes
end;
procedure TNamespace.GetFileTimes;
var
Handle: THandle;
FileDataA: TWin32FindData;
FileDataW: TWin32FindDataW;
S: string;
begin
if not (scFileTimes in ShellCache.ShellCacheFlags) then
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if FileSystem and Assigned(FWin32FindDataW) then
begin
FillChar(FileDataW, SizeOf(FileDataW), #0);
Handle := FindFirstFileW_MP(PWideChar( NameParseAddress), FileDataW);
if Handle <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(Handle); // There is no FindCloseW
FWin32FindDataW.ftLastAccessTime := FileDataW.ftLastAccessTime;
FWin32FindDataW.ftCreationTime := FileDataW.ftCreationTime;
FWin32FindDataW.ftLastWriteTime := FileDataW.ftLastWriteTime
end
end;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if FileSystem and Assigned(FWin32FindDataA) then
begin
FillChar(FileDataA, SizeOf(FileDataA), #0);
S := NameParseAddress;
Handle := FindFirstFileA(PChar( S), FileDataA);
if Handle <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(Handle); // There is no ASCI and Wide version
FWin32FindDataA.ftLastAccessTime := FileDataA.ftLastAccessTime;
FWin32FindDataA.ftCreationTime := FileDataA.ftCreationTime;
FWin32FindDataA.ftLastWriteTime := FileDataA.ftLastWriteTime
end
end;
end;
Include(FShellCache.ShellCacheFlags, scFileTimes)
end;
end;
function TNamespace.GetFileType: WideString;
// File type string shown in column 3 of Explorer Listview
begin
if not (scFileType in ShellCache.ShellCacheFlags) then
begin
if not Assigned(FSHGetFileInfoRec) then
GetSHFileInfo;
if Assigned(FSHGetFileInfoRec) then
begin
FShellCache.Data.FileType := FSHGetFileInfoRec^.FileType;
{ NT only half-assed supports the SHGetFileInfo...only if the ext is }
{ associated with a program. So we build it ourselves }
if FShellCache.Data.FileType = '' then
FShellCache.Data.FileType := WideUpperCase(WideExtractFileExt(NameForParsing)) + STR_FILE;
end else
FShellCache.Data.FileType := '';
Include(FShellCache.ShellCacheFlags, scFileType);
end;
Result := ShellCache.Data.FileType
end;
function TNamespace.GetFolder: Boolean;
// Ask the Folder if it is a Folder, as opposed to files. Folders can contain
// other objects.
begin
if not (scFolder in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_FOLDER, False) then
Include(FShellCache.Data.Attributes, caFolder);
Include(FShellCache.ShellCacheFlags, scFolder);
end;
Result := caFolder in ShellCache.Data.Attributes;
end;
function TNamespace.GetFreePIDLOnDestroy: Boolean;
begin
Result := nsFreePIDLOnDestroy in States
end;
function TNamespace.GetGhosted: Boolean;
// Ask the Folder if it is a ghosted file object. Partially encapsulates the
// IShellFolder.GetAttributesOf function.
begin
if not (scGhosted in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_GHOSTED, False) then
Include(FShellCache.Data.Attributes, caGhosted);
Include(FShellCache.ShellCacheFlags, scGhosted);
end;
Result := caGhosted in ShellCache.Data.Attributes
end;
function TNamespace.GetHasPropSheet: Boolean;
begin
Result := TestAttributesOf(SFGAO_HASPROPSHEET, False);
end;
function TNamespace.GetHasSubFolder: Boolean;
begin
Result := TestAttributesOf(SFGAO_HASSUBFOLDER, False);
end;
function TNamespace.GetHidden: Boolean;
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_HIDDEN <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_HIDDEN <> 0
else
Result := False;
end
end;
function TNamespace.GetIconIndexChanged: Boolean;
begin
Result := nsIconIndexChanged in States
end;
function TNamespace.GetIconIndex(OpenIcon: Boolean; IconSize: TIconSize; ForceLoad: Boolean = True): integer;
{ Retrieve the Icon Index either selected or not selected (open folder or }
{ closed folder) }
function GetIconByIShellIcon(AnOpenIcon: Boolean; Size: TIconSize; var Index: integer): Boolean;
var
Flags: Longword;
begin
Result := False;
if Assigned(ShellIconInterface) then
begin
Flags := 0;
if Size = icLarge then
Flags := GIL_FORSHELL;
if AnOpenIcon then
Flags := GIL_OPENICON or Flags;
Result := ShellIconInterface.GetIconOf(RelativePIDL, Flags, Index) = NOERROR
end
end;
procedure GetIconBySHGetFileInfo(AnOpenIcon: Boolean; Size: TIconSize; var Index: Integer);
{ A little undocumented trick. If you use the SHGFI_USEFILEATTRIBUTES flags }
{ the SHGetFileInfo function does not fully access the object and is much }
{ faster. }
{ UPDATE: Unfortunatly this does not work well in Win98 :^( }
var
Flags: integer;
InfoA: TSHFileInfoA;
InfoW: TSHFileInfoW;
begin
Flags := SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SHELLICONSIZE;
if IconSize = icLarge then
Flags := Flags or SHGFI_LARGEICON
else
Flags := Flags or SHGFI_SMALLICON;
if AnOpenIcon then
Flags := Flags or SHGFI_OPENICON;
if IsUnicode then
begin
FillChar(InfoW, SizeOf(InfoW), #0);
if SHGetFileInfoW_MP(PWideChar(AbsolutePIDL), 0, InfoW, SizeOf(InfoW), Flags) <> 0 then
Index := InfoW.iIcon
else
Index := 0
end else
begin
FillChar(InfoA, SizeOf(InfoA), #0);
if SHGetFileInfoA(PChar(AbsolutePIDL), 0, InfoA, SizeOf(InfoA), Flags) <> 0 then
Index := InfoA.iIcon
else
Index := 0
end
end;
function GetIcon(IsOpen: Boolean; IconSize: TIconSize): integer;
begin
if not GetIconByIShellIcon(IsOpen, IconSize, Result) then
GetIconBySHGetFileInfo(IsOpen, IconSize, Result);
end;
begin
if not OpenIcon then
begin
if not (scSmallIcon in ShellCache.ShellCacheFlags) or ForceLoad then
begin
FShellCache.Data.SmallIcon := GetIcon(False, icSmall);
Include(FShellCache.ShellCacheFlags, scSmallIcon);
end;
Result := ShellCache.Data.SmallIcon;
end else
begin
if not (scSmallOpenIcon in ShellCache.ShellCacheFlags) or ForceLoad then
begin
{ Some Control panel icons return 0 for open but have icons for not open }
{ and it looks bad to show the default icon when the item is selected. }
{ In NT4 some ControlPanel icons are the Mouse icons when selected! }
if Assigned(Parent) and Parent.IsControlPanel then
FShellCache.Data.SmallOpenIcon := GetIcon(False, icSmall)
else begin
FShellCache.Data.SmallOpenIcon := GetIcon(True, icSmall);
{ If it is 0 then try the normal icon }
if FShellCache.Data.SmallOpenIcon = 0 then
FShellCache.Data.SmallOpenIcon := GetIcon(False, icSmall)
end;
Include(FShellCache.ShellCacheFlags, scSmallOpenIcon)
end;
Result := ShellCache.Data.SmallOpenIcon;
end;
end;
function TNamespace.GetInfoTip: WideString;
{ Retrieves the text from the IInfoTip interface in Win2k. }
var
Buffer: PWideChar;
begin
Result := '';
if Assigned(QueryInfoInterface) then
begin
if QueryInfoInterface.GetInfoTip(0, Buffer) = S_OK then
begin
Result := Buffer;
PIDLMgr.FreeOLEStr(Buffer);
end;
end;
end;
function TNamespace.GetLastAccessTime: WideString;
{ GETTER: Last Access time of the file. }
begin
if not (scLastAccessTime in ShellCache.ShellCacheFlags) then
begin
{ Don't use Win32FindData cache, re-read the file times }
GetFileTimes;
if IsUnicode then
begin
if Assigned(FWin32FindDataW) and FileSystem then
FShellCache.Data.LastAccessTime := ConvertTFileTimeToLocalStr(FWin32FindDataW^.ftLastAccessTime)
else
FShellCache.Data.LastAccessTime := '';
end else
begin
if Assigned(FWin32FindDataA) and FileSystem then
FShellCache.Data.LastAccessTime := ConvertTFileTimeToLocalStr(FWin32FindDataA^.ftLastAccessTime)
else
FShellCache.Data.LastAccessTime := '';
end;
Include(FShellCache.ShellCacheFlags, scLastAccessTime);
end;
Result := FShellCache.Data.LastAccessTime
end;
function TNamespace.GetLastAccessDateTime: TDateTime;
begin
Result := ConvertFileTimetoDateTime(LastAccessTimeRaw)
end;
function TNamespace.GetLastAccessTimeRaw: TFileTime;
begin
{ Don't use Win32FindData cache, re-read the file times }
GetFileTimes;
if IsUnicode then
begin
if Assigned(FWin32FindDataW) then
Result := FWin32FindDataW^.ftLastAccessTime
else
FillChar(Result, SizeOf(Result), #0);
end else
begin
if Assigned(FWin32FindDataA) then
Result := FWin32FindDataA^.ftLastAccessTime
else
FillChar(Result, SizeOf(Result), #0);
end
end;
function TNamespace.GetLastWriteTime: WideString;
{ GETTER: Last write time for the file. }
begin
if not (scLastWriteTime in ShellCache.ShellCacheFlags) then
begin
{ Don't use Win32FindData cache, re-read the file times }
GetFileTimes;
if IsUnicode then
begin
if Assigned(FWin32FindDataW) and FileSystem then
FShellCache.Data.LastWriteTime := ConvertTFileTimeToLocalStr(FWin32FindDataW^.ftLastWriteTime)
else
FShellCache.Data.LastWriteTime := '';
end else
begin
if Assigned(FWin32FindDataA) and FileSystem then
FShellCache.Data.LastWriteTime := ConvertTFileTimeToLocalStr(FWin32FindDataA^.ftLastWriteTime)
else
FShellCache.Data.LastWriteTime := '';
end;
Include(FShellCache.ShellCacheFlags, scLastWriteTime);
end;
Result := FShellCache.Data.LastWriteTime
end;
function TNamespace.GetLastWriteDateTime: TDateTime;
begin
Result := ConvertFileTimetoDateTime(LastWriteTimeRaw)
end;
function TNamespace.GetLastWriteTimeRaw: TFileTime;
{ GETTER: Last Write time for the file in raw TFileTime format. }
begin
{ Don't use Win32FindData cache, re-read the file times }
GetFileTimes;
if IsUnicode then
begin
if Assigned(FWin32FindDataW) then
Result := FWin32FindDataW^.ftLastWriteTime
else
FillChar(Result, SizeOf(Result), #0);
end else
begin
if Assigned(FWin32FindDataA) then
Result := FWin32FindDataA^.ftLastWriteTime
else
FillChar(Result, SizeOf(Result), #0);
end
end;
function TNamespace.GetLink: Boolean;
begin
if not (scLink in ShellCache.ShellCacheFlags) then
begin
if TestAttributesOf(SFGAO_LINK, False) then
Include(FShellCache.Data.Attributes, caLink);
Include(FShellCache.ShellCacheFlags, scLink);
end;
Result := caLink in ShellCache.Data.Attributes
end;
function TNamespace.GetNameAddressbar: WideString;
begin
Result := DisplayNameOf(SHGDN_FORADDRESSBAR or SHGDN_NORMAL)
end;
function TNamespace.GetNameAddressbarInFolder: WideString;
begin
Result := DisplayNameOf(SHGDN_INFOLDER or SHGDN_FORADDRESSBAR)
end;
function TNamespace.GetNameForEditing: WideString;
begin
Result := DisplayNameOf(SHGDN_FOREDITING)
end;
function TNamespace.GetNameForEditingInFolder: WideString;
begin
Result := DisplayNameOf(SHGDN_FOREDITING or SHGDN_INFOLDER)
end;
function TNamespace.GetNameForParsing: WideString;
begin
// Early versions of Windows returned "Desktop" instead of the full path
if IsDesktop then
Result := PhysicalDesktopFolder.NameForParsing
else
Result := DisplayNameOf(SHGDN_FORPARSING or SHGDN_NORMAL)
end;
function TNamespace.GetNameForParsingInFolder: WideString;
begin
// Early versions of Windows returned "Desktop" instead of the full path
if IsDesktop then
Result := PhysicalDesktopFolder.NameForParsingInFolder
else
Result := DisplayNameOf(SHGDN_INFOLDER or SHGDN_FORPARSING)
end;
function TNamespace.GetNameInFolder: WideString;
begin
if not (scInFolderName in ShellCache.ShellCacheFlags) then
begin
FShellCache.Data.InFolderName := DisplayNameOf(SHGDN_INFOLDER);
Include(FShellCache.ShellCacheFlags, scInFolderName)
end;
Result := FShellCache.Data.InFolderName
end;
function TNamespace.GetNameNormal: WideString;
begin
if not (scNormalName in ShellCache.ShellCacheFlags) then
begin
FShellCache.Data.NormalName := DisplayNameOf(SHGDN_NORMAL);
Include(FShellCache.ShellCacheFlags, scNormalName)
end;
Result := FShellCache.Data.NormalName
end;
function TNamespace.GetNameParseAddress: WideString;
begin
if not (scParsedName in ShellCache.ShellCacheFlags) then
begin
FShellCache.Data.ParsedName := DisplayNameOf(SHGDN_FORADDRESSBAR or SHGDN_FORPARSING);
Include(FShellCache.ShellCacheFlags, scParsedName)
end;
Result := FShellCache.Data.ParsedName
end;
function TNamespace.GetNameParseAddressInFolder: WideString;
begin
Result := DisplayNameOf(SHGDN_FORADDRESSBAR or SHGDN_FORPARSING or SHGDN_INFOLDER)
end;
function TNamespace.GetNewContent: Boolean;
{ GETTER: Does this namespace contain new content? }
begin
Result := TestAttributesOf(SFGAO_NEWCONTENT, False);
end;
function TNamespace.GetNonEnumerated: Boolean;
{ GETTER: Is this namespace able to be enumerated? }
begin
Result := TestAttributesOf(SFGAO_NONENUMERATED, False);
end;
function TNamespace.GetNormal: Boolean;
{ GETTER: Does the file attributes contain Normal? }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_NORMAL <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_NORMAL <> 0
else
Result := False;
end
end;
function TNamespace.GetOffLine: Boolean;
{ GETTER: Does the file attributes contain OffLine? }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_OFFLINE <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_OFFLINE <> 0
else
Result := False;
end
end;
function TNamespace.GetImage: TBitmap;
begin
Result := FImage;
end;
procedure TNamespace.InvalidateDetailsOfCache(FlushStrings: Boolean);
var
i: Integer;
begin
for i := 0 to Length( ShellCache.Data.DetailsOfCache) - 1 do
begin
ShellCache.Data.DetailsOfCache[i].Cached := [];
if FlushStrings then
ShellCache.Data.DetailsOfCache[i].Caption := ''
end;
FShellCache.ShellCacheFlags := ShellCache.ShellCacheFlags - [scDetailsOfCache];
FShellCache.ShellCacheFlags := ShellCache.ShellCacheFlags - [scInFolderName];
end;
procedure TNamespace.InvalidateThumbImage;
begin
FreeAndNIL(FImage);
Exclude(FStates, nsThreadedImageLoaded);
Exclude(FStates, nsThreadedImageLoading);
end;
function TNamespace.SubFoldersEx(Flags: Longword = SHCONTF_FOLDERS): Boolean;
begin
Result := InternalSubItems(Flags)
end;
function TNamespace.SubItemsEx(Flags: Longword = SHCONTF_NONFOLDERS): Boolean;
begin
Result := InternalSubItems(Flags)
end;
function TNamespace.GetOverlayIconIndex: Integer;
begin
if Assigned(Parent) then
begin
if Assigned(Parent.ShellIconOverlayInterface) then
begin
if FShellCache.Data.OverlayIconIndex < 0 then
begin
if Parent.ShellIconOverlayInterface.GetOverlayIconIndex(FRelativePIDL, FShellCache.Data.OverlayIconIndex) <> S_OK then
FShellCache.Data.OverlayIconIndex := -1;
end
end
end;
Result := FShellCache.Data.OverlayIconIndex
end;
function TNamespace.GetOverlayIndex: Integer;
begin
if Assigned(Parent) then
begin
if not (scOverlayIndex in FShellCache.ShellCacheFlags) then
begin
if Assigned(Parent.ShellIconOverlayInterface) then
begin
if FShellCache.Data.OverlayIndex < 0 then
begin
if Parent.ShellIconOverlayInterface.GetOverlayIndex(FRelativePIDL, FShellCache.Data.OverlayIndex) <> S_OK then
begin
if MP_UseSpecialReparsePointOverlay and IsUnicode and not IsWinNT4 and ReparsePoint then
FShellCache.Data.OverlayIndex := 4
else
FShellCache.Data.OverlayIndex := -1;
end
end
end;
Include(FShellCache.ShellCacheFlags, scOverlayIndex)
end
end;
Result := FShellCache.Data.OverlayIndex;
end;
function TNamespace.GetCanMoniker: Boolean;
begin
Result := TestAttributesOf(SFGAO_CANMONIKER, False)
end;
function TNamespace.GetEncrypted: Boolean;
begin
Result := TestAttributesOf(SFGAO_ENCRYPTED, False)
end;
function TNamespace.GetHasStorage: Boolean;
begin
Result := TestAttributesOf(SFGAO_HASSTORAGE, False)
end;
function TNamespace.GetIsSlow: Boolean;
begin
Result := TestAttributesOf(SFGAO_ISSLOW, False)
end;
function TNamespace.GetStorage: Boolean;
begin
Result := TestAttributesOf(SFGAO_STORAGE, False)
end;
function TNamespace.GetStorageAncestor: Boolean;
begin
Result := TestAttributesOf(SFGAO_STORAGEANCESTOR, False)
end;
function TNamespace.GetStream: Boolean;
begin
Result := TestAttributesOf(SFGAO_STREAM, False)
end;
function TNamespace.GetParentShellDetailsInterface: IVETShellDetails;
begin
{ This forces the Parent to be created if necessary }
if Assigned(ParentShellFolder) then
Result := Parent.ShellDetailsInterface
else
Result := ShellDetailsInterface
end;
function TNamespace.GetParentShellFolder: IShellFolder;
//var
// P: PItemIDList;
begin
Result := Parent.ShellFolder
(* Result := nil;
// 08.31.02
// Going to try to allow the Parent to persist if the namespace creates it itself.
// This is called a LOT more than I thought expecially for the ExplorerListview
if Assigned(Parent) then
begin
Result := Parent.ShellFolder
end else
if not IsDesktop then
begin
if Assigned(Parent) then
FreeAndNil(FParent);
P := PIDLMgr.CopyPIDL(AbsolutePIDL);
FParent := TNamespace.Create(PIDLMgr.StripLastID(P), nil);
if Assigned(FParent) then
begin
Result := FParent.ShellFolder;
{ Since we created the parent we own it in case we have to destroy it in }
{ our destructor. }
Include(FStates, nsOwnsParent);
end
end else
Result := ShellFolder *)
end;
function TNamespace.GetParentShellFolder2: IShellFolder2;
begin
{ This flag keeps us from constantly trying to get FShellFolder2 if it is not }
{ supported by the namespace. }
{ This forces the Parent to be created if necessary }
if Assigned(ParentShellFolder) then
Result := Parent.ShellFolder2
else
Result := ShellFolder2
end;
function TNamespace.GetQueryInfoInterface: IQueryInfo;
var
Found: Boolean;
begin
if not Assigned(FQueryInfoInterface) then
begin
Found := False;
if Assigned(ParentShellFolder) then
begin
Found := ParentShellFolder.GetUIObjectOf(ParentWnd, 1, FRelativePIDL,
IQueryInfo, nil, Pointer(FQueryInfoInterface)) = NOERROR;
end;
if not Found and Assigned(ShellFolder) then
begin
Found := ShellFolder.CreateViewObject(ParentWnd, IQueryInfo,
Pointer(FQueryInfoInterface)) = NOERROR;
end;
if not Found and IsDesktop then
FQueryInfoInterface := PhysicalDesktopFolder.QueryInfoInterface;
end;
Result := FQueryInfoInterface
end;
function TNamespace.GetReadOnly: Boolean;
{ GETTER: Is this namespace ReadOnly? }
begin
Result := TestAttributesOf(SFGAO_READONLY, False);
end;
function TNamespace.GetReadOnlyFile: Boolean;
{ GETTER: Does the file attributes contain ReadOnly? }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_READONLY <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_READONLY <> 0
else
Result := False;
end
end;
function TNamespace.GetReparsePoint: Boolean;
{ GETTER: Does the file attributes contain ReadOnly? }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT <> 0
else
Result := False;
end
end;
function TNamespace.GetRemovable: Boolean;
{ GETTER: Is this a removeable object? }
begin
Result := TestAttributesOf(SFGAO_REMOVABLE, False);
end;
function TNamespace.GetShellDetailsInterface: IVETShellDetails;
var
Found: Boolean;
begin
{ This flag keeps us from constantly trying to get IShellDetails if it is not }
{ supported by the namespace. }
if (nsShellDetailsSupported in States) and not Assigned(FShellDetailsInterface) then
begin
Found := False;
if not Found and Assigned(ShellFolder) then
Found := ShellFolder.CreateViewObject(ParentWnd, IID_IShellDetails, Pointer(FShellDetailsInterface)) = NOERROR;
if not Found and Assigned(ParentShellFolder) then
Found := ParentShellFolder.GetUIObjectOf(ParentWnd, 1, FRelativePIDL, IID_IShellDetails, nil,
Pointer(FShellDetailsInterface)) = NOERROR;
if Found then
Include(FStates, nsShellDetailsSupported)
else
begin
Exclude(FStates, nsShellDetailsSupported);
FShellDetailsInterface := nil
end
end;
Result := FShellDetailsInterface;
end;
function TNamespace.GetShellIconInterface: IShellIcon;
var
Found: Boolean;
begin
if not Assigned(FShellIconInterface) then
begin
Found := False;
if Assigned(ParentShellFolder) then
Found := ParentShellFolder.QueryInterface(IID_IShellIcon,
Pointer(FShellIconInterface)) <> E_NOINTERFACE;
if not Found then
FShellIconInterface := nil
end;
Result := FShellIconInterface
end;
function TNamespace.GetShellFolder: IShellFolder;
var
P: PItemIDList;
ParentFolder, Desktop: IShellFolder;
begin
if not Assigned(FShellFolder) then
begin
SHGetDesktopFolder(Desktop);
if PIDLMgr.IDCount(AbsolutePIDL) > 1 then
begin
P := PIDLMgr.StripLastID(PIDLMgr.CopyPIDL(AbsolutePIDL));
Desktop.BindToObject(P, nil, IID_IShellFolder, Pointer(ParentFolder));
PIDLMgr.FreePIDL(P)
end else
ParentFolder := Desktop;
if Assigned(ParentFolder) then
begin
if IsDesktop or not Folder then
FShellFolder := ParentFolder
else begin
if not Succeeded(ParentFolder.BindToObject(FRelativePIDL, nil, IID_IShellFolder, Pointer(FShellFolder))) then
FShellFolder := nil
end
end else
FShellFolder := nil
end;
Result := FShellFolder
end;
function TNamespace.GetShellFolder2: IShellFolder2;
begin
{ This flag keeps us from constantly trying to get FShellFolder2 if it is not }
{ supported by the namespace. }
if (nsShellFolder2Supported in States) and not Assigned(FShellFolder2) then
begin
if Assigned(ShellFolder) then
if ShellFolder.QueryInterface(IID_IShellFolder2, Pointer(FShellFolder2)) = E_NOINTERFACE
then begin
FShellFolder2 := nil;
Exclude(FStates, nsShellFolder2Supported)
end else
Include(FStates, nsShellFolder2Supported)
end;
Result := FShellFolder2;
end;
function TNamespace.GetShellLink: TVirtualShellLink;
begin
if Link then
begin
if not Assigned(FShellLink) then
FShellLink := TVirtualShellLink.Create(nil);
FShellLink.ReadLink(NameParseAddress);
end;
Result := FShellLink
end;
function TNamespace.GetSizeOfFile: WideString;
{ GETTER: Get the size of the file in string format}
begin
if not (scFileSize in ShellCache.ShellCacheFlags) then
begin
if not Folder then
begin
FShellCache.Data.FileSize := Format('%0.0n', [SizeOfFileInt64 + 0.0]);
// FShellCache.Data.FileSize := AddCommas(WideIntToStr(SizeOfFileInt64));
Include(FShellCache.ShellCacheFlags, scFileSize);
end else
FShellCache.Data.FileSize := ''
end;
Result := ShellCache.Data.FileSize
end;
function TNamespace.GetSizeOfFileDiskUsage: WideString;
var
Size, BytesPerCluster: Int64;
Drive: string;
DriveW: WideString;
SectorsPerCluster,
BytesPerSector,
FreeClusters,
TotalClusters,
i : DWORD;
ValidData: Boolean;
begin
if not Folder then
begin
Size := SizeOfFileInt64;
DriveW := WideExtractFileDrive(Self.NameForParsing) + '\';
Drive := DriveW;
if WideDirectoryExists(Drive) then
begin
ValidData := GetDiskFreeSpaceMP(PWideChar( DriveW), SectorsPerCluster, BytesPerSector, FreeClusters, TotalClusters);
if ValidData then
begin
BytesPerCluster := Int64(SectorsPerCluster) * Int64(BytesPerSector);
if BytesPerCluster <> 0 then
begin
{ In the *rare* instance where the actual size is equal to multiple of }
{ the sector size don't do the math :^) }
if Size mod BytesPerCluster <> 0 then
i := 1
else
i := 0;
Result := Format('%0.0n', [Int64(BytesPerCluster) *(Size div Int64(BytesPerCluster) + i) + 0.0])
end else
Result := SizeOfFile
end else
Result := SizeOfFile
end else
Result := SizeOfFile
end;
end;
function TNamespace.GetSizeOfFileInt64: Int64;
var
H: THandle;
FindDataW: TWin32FindDataW;
FindDataA: TWin32FindDataA;
{ GETTER: Get the file size in bytes. }
// The PIDL does not store the file size for > 4G files, need to use FindFirstFile
begin
if not (scFileSizeInt64 in ShellCache.ShellCacheFlags) then
begin
if FileSystem then
begin
if IsUnicode then
begin
H := FindFirstFileW_MP(PWideChar(NameForParsing), FindDataW);
if H <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(H);
FShellCache.Data.FileSizeInt64 := FindDataW.nFileSizeLow;
if FShellCache.Data.FileSizeInt64 < 0 then
FShellCache.Data.FileSizeInt64 := FShellCache.Data.FileSizeInt64 + 4294967296;
if FindDataW.nFileSizeHigh > 0 then
FShellCache.Data.FileSizeInt64 := FShellCache.Data.FileSizeInt64 + (FindDataW.nFileSizeHigh * 4294967296)
end
end else
begin
H := FindFirstFileA(PChar(string(NameForParsing)), FindDataA);
if H <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(H);
FShellCache.Data.FileSizeInt64 := FindDataA.nFileSizeLow;
if FShellCache.Data.FileSizeInt64 < 0 then
FShellCache.Data.FileSizeInt64 := FShellCache.Data.FileSizeInt64 + 4294967296;
if FindDataA.nFileSizeHigh > 0 then
FShellCache.Data.FileSizeInt64 := FShellCache.Data.FileSizeInt64 + (FindDataA.nFileSizeHigh * 4294967296)
end
end
end;
Include(FShellCache.ShellCacheFlags, scFileSizeInt64)
end;
Result := FShellCache.Data.FileSizeInt64
end;
function TNamespace.GetSizeOfFileKB: WideString;
{ GETTER: Get the file size in Explorer KiloByte format. }
begin
if not (scFileSizeKB in ShellCache.ShellCacheFlags) then
begin
if not Folder then
begin
if SizeOfFileInt64 > 0 then
begin
FShellCache.Data.FileSizeKB := Format('%0.0n '+ 'KB', [SizeOfFileInt64/1024]);
if (FShellCache.Data.FileSizeKB = STR_ZERO_KB) then
FShellCache.Data.FileSizeKB := STR_ONE_KB;
end else
FShellCache.Data.FileSizeKB := STR_ONE_KB;
Include(FShellCache.ShellCacheFlags, scFileSizeKB)
end else
FShellCache.Data.FileSizeKB := '';
end;
Result := FShellCache.Data.FileSizeKB;
end;
function TNamespace.GetSparseFile: Boolean;
{ GETTER: Does the file attributes contain ReadOnly? }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_SPARSE_FILE <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_SPARSE_FILE <> 0
else
Result := False;
end
end;
function TNamespace.GetShare: Boolean;
begin
Result := TestAttributesOf(SFGAO_SHARE, False);
end;
procedure TNamespace.GetSHFileInfo;
{ Retrieves and caches the some information about the namespace with }
{ ShGetFileInfo. }
var
InfoA: TSHFileInfoA;
InfoW: TSHFileInfoW;
begin
if not Assigned(FSHGetFileInfoRec) then
begin
if IsUnicode then
begin
GetMem(FSHGetFileInfoRec, SizeOf(FSHGetFileInfoRec^));
Initialize(FSHGetFileInfoRec^.FileType);
if Assigned(FSHGetFileInfoRec) then
begin
SHGetFileInfoW_MP(PWideChar(AbsolutePIDL), 0, InfoW, SizeOf(InfoW), SHGFI_TYPENAME or SHGFI_PIDL);
FSHGetFileInfoRec^.FileType := InfoW.szTypeName;
{ NT only half-assed supports the SHGetFileInfo...only if the ext is }
{ associated with a program. So we build it ourselves }
if FSHGetFileInfoRec^.FileType = '' then
FSHGetFileInfoRec^.FileType := WideUpperCase(WideExtractFileExt(NameForParsing)) + STR_FILE;
end
end else
begin
GetMem(FSHGetFileInfoRec, SizeOf(FSHGetFileInfoRec^));
Initialize(FSHGetFileInfoRec^.FileType);
if Assigned(FSHGetFileInfoRec) then
begin
SHGetFileInfoA(PChar(AbsolutePIDL), 0, InfoA, SizeOf(InfoA), SHGFI_TYPENAME or SHGFI_PIDL);
FSHGetFileInfoRec^.FileType := InfoA.szTypeName;
{ NT only half-assed supports the SHGetFileInfo...only if the ext is }
{ associated with a program. So we build it ourselves }
if FSHGetFileInfoRec^.FileType = '' then
FSHGetFileInfoRec^.FileType := WideUpperCase(WideExtractFileExt(NameForParsing)) + STR_FILE;
end
end;
end;
end;
function TNamespace.GetShortFileName: WideString;
{ GETTER: Get the 8:3 short file name (DOS) }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
begin
Result := FWin32FindDataW^.cAlternateFileName;
if Result = '' then
Result := WideUpperCase(FWin32FindDataW^.CFileName)
end else
Result := '';
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
begin
Result := FWin32FindDataA^.cAlternateFileName;
if Result = '' then
Result := WideUpperCase(FWin32FindDataA^.CFileName)
end else
Result := '';
end
end;
function TNamespace.GetSubFolders: Boolean;
{ Tests to see if a namespace is a true folder and has at least one }
{ sub-namespace within it. }
begin
Result := InternalSubItems(SHCONTF_FOLDERS or SHCONTF_INCLUDEHIDDEN)
end;
function TNamespace.GetSubItems: Boolean;
{ Tests to see if a namespace is a true folder and has at least one }
{ sub-namespace within it. }
begin
Result := InternalSubItems(SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN)
end;
function TNamespace.GetSystem: Boolean;
{ GETTER: Does the file attributes contain System? }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_SYSTEM <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_SYSTEM <> 0
else
Result := False;
end
end;
function TNamespace.GetTemporary: Boolean;
{ GETTER: Does the file attributes contain Temporary? }
begin
if IsUnicode then
begin
if not Assigned(FWin32FindDataW) then
GetDataFromIDList;
if Assigned(FWin32FindDataW) and FileSystem then
Result := FWin32FindDataW^.dwFileAttributes and FILE_ATTRIBUTE_TEMPORARY <> 0
else
Result := False;
end else
begin
if not Assigned(FWin32FindDataA) then
GetDataFromIDList;
if Assigned(FWin32FindDataA) and FileSystem then
Result := FWin32FindDataA^.dwFileAttributes and FILE_ATTRIBUTE_TEMPORARY <> 0
else
Result := False;
end
end;
function TNamespace.GetThreadedIconLoaded: Boolean;
begin
Result := nsThreadedIconLoaded in States
end;
function TNamespace.GetThreadIconLoading: Boolean;
begin
Result := nsThreadedIconLoading in States
end;
function TNamespace.GetShellIconOverlayInterface: IShellIconOverlay;
var
Found: Boolean;
begin
if (nsShellOverlaySupported in States) and not Assigned(FShellIconOverlayInterface) then
begin
Found := False;
if Assigned(ShellFolder) then
Found := ShellFolder.QueryInterface(IShellIconOverlay,
Pointer(FShellIconOverlayInterface)) <> E_NOINTERFACE;
if not Found then // Here we have to check again
begin
IF Assigned(ParentShellFolder) then
Found := ParentShellFolder.QueryInterface(IShellIconOverlay,
Pointer(FShellIconOverlayInterface)) <> E_NOINTERFACE;
end;
if not Found then
begin
Exclude(FStates, nsShellOverlaySupported);
FShellIconOverlayInterface := nil
end
end;
Result := FShellIconOverlayInterface
end;
procedure TNamespace.HandleContextMenuMsg(Msg, wParam, lParam: Longint; var Result: LRESULT);
{ This is called when the ContextMenu calls back to its owner window to ask }
{ questions to implement the addition of icons to the menu. The messages sent }
{ to the owner window are: WM_INITMENUPOPUP, WM_DRAWITEM, or WM_MEASUREITEM. }
{ Which must be passed on to the ContextMenu2 interface to display items with }
{ icons. }
var
ContextMenu3: IContextMenu3;
begin
if Assigned(CurrentContextMenu2) then
if CurrentContextMenu2.QueryInterface(IContextMenu3, ContextMenu3) <> E_NOINTERFACE then
ContextMenu3.HandleMenuMsg2(Msg, wParam, lParam, Result)
else
CurrentContextMenu2.HandleMenuMsg(Msg, wParam, lParam);
end;
function TNamespace.InjectCustomSubMenu(Menu: HMenu; Caption: string; PopupMenu: TPopupMenu;
var SubMenu: HMenu): TMenuItemIDArray;
const
MENUMASK = MIIM_CHECKMARKS or MIIM_DATA or MIIM_ID or MIIM_STATE or MIIM_TYPE;
{ Searchs through the passed menu looking for an item identifer that is not }
{ currently being used. }
function FindUniqueMenuID(AMenu: HMenu; StartID: cardinal): cardinal;
var
ItemCount, i: integer;
Duplicate, Done: Boolean;
begin
ItemCount := GetMenuItemCount(Menu);
Duplicate := True;
Result := StartID;
while Duplicate do
begin
i := 0;
Done := False;
while (i < ItemCount) and not Done do
begin
Done := GetMenuItemID(Menu, i) = Result;
Inc(i);
end;
Duplicate := Done;
if Duplicate then
Inc(Result)
end;
end;
var
ItemCount, i: integer;
ItemInfo: TMenuItemInfo;
LastID: cardinal;
begin
Result := nil;
SubMenu := 0;
LastID := 0;
ItemCount := GetMenuItemCount(Menu);
SubMenu := CreatePopupMenu;
FillChar(ItemInfo, SizeOf(ItemInfo), #0);
ItemInfo.cbSize := SizeOf(ItemInfo);
ItemInfo.fmask := MIIM_TYPE;
ItemInfo.fType := MFT_SEPARATOR;
InsertMenuItem(Menu, ItemCount, True, ItemInfo);
FillChar(ItemInfo, SizeOf(ItemInfo), #0);
ItemInfo.cbSize := SizeOf(ItemInfo);
ItemInfo.fmask := MIIM_SUBMENU or MIIM_TYPE;
ItemInfo.hSubMenu := SubMenu;
ItemInfo.dwTypeData := PChar(Caption);
// Insert the Root Menu Item
if InsertMenuItem(Menu, ItemCount + 1, True, ItemInfo) then
begin
SetLength(Result, PopupMenu.Items.Count);
for i := PopupMenu.Items.Count - 1 downto 0 do
begin
FillChar(ItemInfo, SizeOf(ItemInfo), #0);
ItemInfo.cbSize := SizeOf(ItemInfo);
ItemInfo.fmask := MENUMASK;
if PopupMenu.Items[i].Caption <> '-' then
ItemInfo.fType := MFT_STRING
else
ItemInfo.fType := MFT_SEPARATOR;
if PopupMenu.Items[i].RadioItem then
ItemInfo.fType := ItemInfo.fType or MFT_RADIOCHECK;
if PopupMenu.BiDiMode = bdRightToLeft then
ItemInfo.fType := ItemInfo.fType or MFT_RIGHTJUSTIFY;
if PopupMenu.Items[i].Break = mbBreak then
ItemInfo.fType := ItemInfo.fType or MFT_MENUBREAK;
if PopupMenu.Items[i].Break = mbBarBreak then
ItemInfo.fType := ItemInfo.fType or MFT_MENUBARBREAK;
if PopupMenu.Items[i].Checked then
ItemInfo.fState := ItemInfo.fState or MFS_CHECKED
else
ItemInfo.fState := ItemInfo.fState or MFS_UNCHECKED;
if PopupMenu.Items[i].Default then
ItemInfo.fState := ItemInfo.fState or MFS_DEFAULT;
if PopupMenu.Items[i].Enabled then
ItemInfo.fState := ItemInfo.fState or MFS_ENABLED
else
ItemInfo.fState := ItemInfo.fState or MFS_DISABLED;
ItemInfo.wID := FindUniqueMenuID(Menu, LastID + 1);
LastID := ItemInfo.wID;
Result[i] := ItemInfo.wID;
// Store the TMenuItem so we can get it later
ItemInfo.dwItemData := Cardinal( PopupMenu.Items[i]);
if not( ItemInfo.fType and MFT_SEPARATOR <> 0) then
ItemInfo.dwTypeData := PChar(PopupMenu.Items[i].Caption);
InsertMenuItem(SubMenu, 0, True, ItemInfo)
end
end
end;
function TNamespace.InternalGetContextMenuInterface(PIDLArray: TRelativePIDLArray): IContextMenu;
var
Found: Boolean;
begin
Found := False;
CurrentContextMenu2 := nil; // Clear since not sure if it is avaiable yet
if Assigned(PIDLArray) then
begin
if Assigned(ParentShellFolder) then
begin
Found := Succeeded(ParentShellFolder.GetUIObjectOf(ParentWnd,
Length(PIDLArray), PItemIDList( PIDLArray[0]),
IID_IContextMenu, nil, Pointer(Result)))
end;
if not Found and Assigned(ShellFolder) and (Length(PIDLArray) = 1) then
begin
Found := ShellFolder.CreateViewObject(ParentWnd, IID_IContextMenu,
Pointer(Result)) = NOERROR;
end;
if not Found then
Result := nil
end else
Result := nil
end;
function TNamespace.InternalGetDataObjectInterface(PIDLArray: TRelativePIDLArray): IDataObject;
{ Creates an IDataObject using the passed relative PIDLs (actually siblings of }
{ the TNamespace) If a nil is passed for PIDLArray a single object based on }
{ TNamespace is created. }
var
Found: Boolean;
begin
if not Assigned(PIDLArray) then
begin
SetLength(PIDLArray, 1);
PIDLArray[0] := RelativePIDL
end;
Found := False;
if Assigned(PIDLArray) then
begin
if Assigned(ParentShellFolder) then
begin
Found := ParentShellFolder.GetUIObjectOf(ParentWnd,
Length(PIDLArray), PItemIDList( PIDLArray[0]),
IDataObject, nil, Pointer(Result)) = NOERROR;
end;
if not Found and Assigned(ShellFolder) and (Length(PIDLArray) = 1) then
begin
Found := ShellFolder.CreateViewObject(ParentWnd, IDataObject,
Pointer(Result)) = NOERROR;
end;
if not Found then
Result := nil
end else
Result := nil
end;
function TNamespace.InternalShowContextMenu(Owner: TWinControl;
ContextMenuCmdCallback: TContextMenuCmdCallback;
ContextMenuShowCallback: TContextMenuShowCallback;
ContextMenuAfterCmdCallback: TContextMenuAfterCmdCallback;
PIDLArray: TRelativePIDLArray; Position: PPoint; CustomShellSubMenu: TPopupMenu;
CustomSubMenuCaption: WideString): Boolean;
// Displays the ContextMenu of the namespace.
const
MaxVerbLen = 128;
var
Menu: hMenu;
InvokeInfo: TCMInvokeCommandInfoEx;
MenuCmd: Cardinal;
x, y, i: integer;
OldErrorMode: integer;
VerbA: string;
VerbW: WideString;
GenericVerb: Pointer;
Handled, AllowShow: Boolean;
Flags: Longword;
ContextMenu: IContextMenu;
ContextMenu2: IContextMenu2;
ContextMenu3: IContextMenu3;
MenuIDs: TMenuItemIDArray;
ItemInfo: TMenuItemInfo;
SubMenu: HMenu;
OldMode: UINT;
begin
OldMode := SetErrorMode(SEM_FAILCRITICALERRORS);
try
MenuIDs := nil;
Result := False;
Assert(Assigned(Owner), 'To show a Context Menu using TNamespace you must pass a valid Owner TWinControl');
if Assigned(Owner) then
begin
FOldWndProcForContextMenu := Owner.WindowProc;
try
// Hook the owner for the Window message for owner draw menus like
// Send To..
Owner.WindowProc := WindowProcForContextMenu;
if Assigned(PIDLArray) then
begin
ContextMenu := nil;
ContextMenu2 := nil;
ContextMenu3 := nil;
Result := False;
if Assigned(Position) then
begin
x := Position.x;
y := Position.y
end else
begin
x := Mouse.CursorPos.X; // Snag these fast. The mouse can move a fair amount
y := Mouse.CursorPos.Y; // before the popup menu is shown.
end;
FillChar(InvokeInfo, SizeOf(InvokeInfo), #0);
Menu := CreatePopupMenu;
OldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
try
{ The application must handle a rename, rename makes no sense for more than 1 item }
if Assigned(ContextMenuCmdCallback) and (Length(PIDLArray) = 1) then
Flags := CMF_CANRENAME or CMF_NORMAL or CMF_EXPLORE
else
Flags := CMF_NORMAL or CMF_EXPLORE;
if GetKeyState(VK_SHIFT) and $8000 <> 0 then
Flags := Flags or CMF_EXTENDEDVERBS;
if Assigned(PIDLArray) then
ContextMenu := InternalGetContextMenuInterface(PIDLArray)
else
ContextMenu := ContextMenuInterface;
CurrentContextMenu := ContextMenu;
CurrentContextMenu2 := nil; // not sure it is available yet
if Assigned(ContextMenu) then
begin
if ContextMenu.QueryInterface(IContextMenu3, Pointer(ContextMenu3)) = E_NOINTERFACE then
begin
if ContextMenu.QueryInterface(IID_IContextMenu2, Pointer(ContextMenu2)) <> E_NOINTERFACE then
CurrentContextMenu2 := ContextMenu2;
end else
CurrentContextMenu2 := ContextMenu3;
if Assigned(ContextMenu3) then
ContextMenu3.QueryContextMenu(Menu, 0, 1, $7FFF, Flags)
else
if Assigned(ContextMenu2) then
ContextMenu2.QueryContextMenu(Menu, 0, 1, $7FFF, Flags)
else
if Assigned(ContextMenu) then
ContextMenu.QueryContextMenu(Menu, 0, 1, $7FFF, Flags);
// Inject our custom menu item
if Assigned(CustomShellSubMenu) then
MenuIDs := InjectCustomSubMenu(Menu, CustomSubMenuCaption, CustomShellSubMenu, SubMenu);
AllowShow := True;
if Assigned(ContextMenuShowCallback) then
ContextMenuShowCallback(Self, Menu, AllowShow);
if AllowShow then
MenuCmd := Cardinal( TrackPopupMenuEx(
Menu,
TPM_LEFTALIGN or TPM_RETURNCMD or TPM_RIGHTBUTTON,
x, y, Owner.Handle, nil))
else
MenuCmd := 0;
if MenuCmd <> 0 then
begin
if IsUnicode then
begin
SetLength(VerbW, MaxVerbLen);
GenericVerb := @VerbW[1];
Flags := GCS_VERBW
end else
begin
SetLength(VerbA, MaxVerbLen);
GenericVerb := @VerbA[1];
Flags := GCS_VERBA
end;
if Assigned(ContextMenu3) then
Result := Succeeded(ContextMenu3.GetCommandString(MenuCmd-1, Flags, nil, GenericVerb, MaxVerbLen))
else
if Assigned(ContextMenu2) then
Result := Succeeded(ContextMenu2.GetCommandString(MenuCmd-1, Flags, nil, GenericVerb, MaxVerbLen))
else
if Assigned(ContextMenu) then
Result := Succeeded(ContextMenu.GetCommandString(MenuCmd-1, Flags, nil, GenericVerb, MaxVerbLen));
if IsUnicode then
SetLength(VerbW, lstrlenW(PWideChar( VerbW)))
else begin
SetLength(VerbA, StrLen(PChar( VerbA)));
VerbW := VerbA
end;
if not Result then
VerbW := STR_UNKNOWNCOMMAN;
Handled := False;
for i := 0 to Length(MenuIDs) - 1 do
begin
if MenuCmd = MenuIDs[i] then
begin
if SubMenu <> 0 then
begin
Handled := True;
FillChar(ItemInfo, SizeOf(ItemInfo), #0);
ItemInfo.cbSize := SizeOf(TMenuItemInfo);
ItemInfo.fMask := MIIM_DATA;
GetMenuItemInfo(SubMenu, i, True, ItemInfo);
if ItemInfo.dwItemData <> 0 then
TMenuItem(ItemInfo.dwItemData).Click
end
end
end;
if not Handled then
if Assigned(ContextMenuCmdCallback) then
ContextMenuCmdCallBack(Self, VerbW, MenuCmd, Handled);
if not Handled then
begin
FillChar(InvokeInfo, SizeOf(InvokeInfo), #0);
with InvokeInfo do
begin
{ For some reason the lpVerbW won't work }
lpVerb := MakeIntResourceA(MenuCmd-1);
if IsUnicode then
begin
fMask := CMIC_MASK_UNICODE;
lpVerbW := MakeIntResourceW(MenuCmd-1)
end;
// Win95 get confused if size = TCMInvokeCommandInfoEx
if IsUnicode then
cbSize := SizeOf(TCMInvokeCommandInfoEx)
else
cbSize := SizeOf(TCMInvokeCommandInfo);
hWnd := Owner.Handle;
nShow := SW_SHOWNORMAL;
end;
if Assigned(ContextMenu3) then
Result := Succeeded(ContextMenu3.InvokeCommand(InvokeInfo))
else
if Assigned(ContextMenu2) then
Result := Succeeded(ContextMenu2.InvokeCommand(InvokeInfo))
else
if Assigned(ContextMenu) then
Result := Succeeded(ContextMenu.InvokeCommand(InvokeInfo));
end
end;
if Assigned(ContextMenuAfterCmdCallback) then
ContextMenuAfterCmdCallback(Self, VerbW, MenuCmd, Result);
end;
finally
{ Don't access any properties or field of the object. If the verb is }
{ 'delete' the component using this class could have freed the instance }
{ of the object through a ShellNotifyRegister or some other way. }
DestroyMenu(Menu);
SetErrorMode(OldErrorMode);
end
end
finally
Owner.WindowProc := FOldWndProcForContextMenu;
FOldWndProcForContextMenu := nil;
// Don't nil until after the hook is unset
CurrentContextMenu := nil;
CurrentContextMenu2 := nil; // not sure it is available yet
end
end
finally
SetErrorMode(OldMode)
end
end;
function TNamespace.OkToBrowse(ShowExplorerMsg: Boolean): Boolean;
var
S: WideString;
begin
Result := True;
S := NameForParsing;
if ((Length(S) = 3) and (S[2] = ':') and (S[3] = '\')) then
Result := DiskInDrive(Char(S[1]));
if not Result and ShowExplorerMsg then
EnumerateFolderEx(ParentWnd, [foFolders, foNonFolders, foHidden], nil, nil)
end;
function TNamespace.InternalSubItems(Flags: Longword): Boolean;
{ Tests to see if a namespace is a true folder and has at least one }
{ sub-namespace within it. }
var
Enum: IEnumIDList;
Fetched: Longword;
Item: PItemIDList;
OldError: DWORD;
begin
Result := False;
OldError := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
try
{ The recycle bin enumerates slow if it is full. VT will InitNode for various }
{ reasons eventhough the node is not expanded. We will always assume there is }
{ is something in the bin. If not when it is clicked it will clear the "+" }
if IsRecycleBin then
Result := True
else
// if ILIsParent(HistoryFolder.AbsolutePIDL, AbsolutePIDL, False) then
// Result := True
// else
begin
if Folder and Assigned(ShellFolder) then
begin
Item := nil;
Enum := nil;
// Since this is just an internal test don't show any dialogs so send a 0
if ShellFolder.EnumObjects(0, Flags, Enum) = NOERROR then
begin
// Vista Enum is nil every once in a while
if Assigned(Enum) then
begin
Result := Enum.Next(1, Item, Fetched) = NOERROR;
if Assigned(Item) then
PIDLMgr.FreePIDL(Item)
end
end
end
end
finally
SetErrorMode(OldError);
end
end;
procedure TNamespace.InvalidateCache;
{ Forces the class to reload any cached data the next time it is retrieved. }
begin
FShellCache.Data.InFolderName := '';
FShellCache.Data.NormalName := '';
FShellCache.Data.ParsedName := '';
FShellCache.Data.SmallIcon := -1;
FShellCache.Data.SmallOpenIcon := -1;
FShellCache.Data.OverlayIndex := -1;
FShellCache.Data.OverlayIconIndex := -1;
FShellCache.Data.CreationTime := '';
FShellCache.Data.LastAccessTime := '';
FShellCache.Data.LastWriteTime := '';
FShellCache.Data.FileSize := '';
FShellCache.Data.FileSizeKB := '';
FShellCache.Data.FileType := '';
FShellCache.Data.FileSizeInt64 := 0;
FShellCache.Data.SupportedColumns := 0;
FShellCache.Data.Attributes := [];
SetLength(FShellCache.Data.DetailsOfCache, 0);
FShellCache.ShellCacheFlags := [];
FreeAndNil(FExtractImage);
FreeAndNil(FShellLink);
InvalidateThumbImage
end;
procedure TNamespace.InvalidateNamespace(RefreshIcon: Boolean = True);
var
Icon1, Icon2: integer;
Icon1Initialized, Icon2Initialized: Boolean;
begin
Icon1 := 0;
Icon2 := 0;
Icon1Initialized := False;
Icon2Initialized := False;
if not RefreshIcon then
begin
if scSmallIcon in FShellCache.ShellCacheFlags then
begin
Icon1Initialized := True;
Icon1 := FShellCache.Data.SmallIcon;
end;
if scSmallOpenIcon in FShellCache.ShellCacheFlags then
begin
Icon2Initialized := True;
Icon2 := FShellCache.Data.SmallOpenIcon;
end;
end else
begin
// Flush the thread state so the icon is reloaded by the thread
States := States - [nsThreadedIconLoading];
States := States - [nsThreadedIconLoaded];
end;
InvalidateDetailsOfCache(True);
InvalidateCache;
if nsOwnsParent in States then
FreeAndNil(FParent);
FDropTargetInterface := nil;
FShellDetailsInterface := nil;
FShellIconOverlayInterface := nil;
FShellFolder := nil;
if Assigned(Parent) then
if Parent.IsDesktop then
PhysicalDesktopFolder.InvalidateNamespace;
if IsUnicode then
begin
if Assigned(FWin32FindDataW) then
FreeMem(FWin32FindDataW, SizeOf(TWin32FindDataW));
FWin32FindDataW := nil;
end else
begin
if Assigned(FWin32FindDataA) then
FreeMem(FWin32FindDataA, SizeOf(TWin32FindDataA));
FWin32FindDataA := nil;
end;
if Assigned(FSHGetFileInfoRec) then
begin
Finalize(FSHGetFileInfoRec^);
FreeMem(FSHGetFileInfoRec, SizeOf(TSHGetFileInfoRec));
end;
FSHGetFileInfoRec := nil;
Include(FStates, nsShellDetailsSupported); // Be optomistic
Include(FStates, nsShellFolder2Supported); // Be optomistic
FQueryInfoInterface := nil;
FShellIconInterface := nil;
FCurrentContextMenu2 := nil;
if not RefreshIcon then
begin
if Icon1Initialized then
begin
Include(FShellCache.ShellCacheFlags, scSmallIcon);
FShellCache.Data.SmallIcon := Icon1;
end;
if Icon2Initialized then
begin
Include(FShellCache.ShellCacheFlags, scSmallOpenIcon);
FShellCache.Data.SmallOpenIcon := Icon2;
end;
end;
end;
procedure TNamespace.InvalidateRelativePIDL(FileObjects: TFileObjects);
var
Enum: IEnumIDList;
Flags: Longword;
Fetched: Longword;
Item: PItemIDList;
Done: Boolean;
begin
if Assigned(ParentShellFolder) then
begin
Flags := FileObjectsToFlags(FileObjects);
Done := False;
if ParentShellFolder.EnumObjects(ParentWnd, Flags, Enum) = NOERROR then
begin
// Vista Enum is nil every once in a while
if Assigned(Enum) then
begin
while (Enum.Next(1, Item, Fetched) = NOERROR) and not Done do
begin
if ComparePIDL(Item, False) = 0 then
begin
PIDLMgr.FreePIDL(FAbsolutePIDL);
FAbsolutePIDL := PIDLMgr.AppendPIDL(Parent.AbsolutePIDL, Item);
FRelativePIDL := PIDLMgr.GetPointerToLastID(FAbsolutePIDL);
InvalidateNamespace;
Done := True
end;
PIDLMgr.FreePIDL(Item)
end
end
end
end
end;
function TNamespace.IsChildByNamespace(TestNamespace: TNamespace;
Immediate: Boolean): Boolean;
{ Returns True if the TestNamespace is a child of the namespace. Immediate }
{ forces function to be true only of the passed PIDL is the immidiate child }
{ of the namespace. }
begin
Result := Boolean( ILIsParent(FAbsolutePIDL, TestNamespace.FAbsolutePIDL, Immediate));
end;
function TNamespace.IsChildByPIDL(TestPIDL: PItemIDList;
Immediate: Boolean): Boolean;
{ Returns True if the TestPIDL is a child of the namespace. Immediate forces }
{ function to be true only of the passed PIDL is the immidiate child of the }
{ namespace. }
begin
Result := Boolean( ILIsParent(FAbsolutePIDL, TestPIDL, Immediate));
end;
function TNamespace.IsChildOfRemovableDrive: Boolean;
// Checks to see if the namespace is a child of a removable drive. If the drive
// is removed then ILIsParent fails because the drive is no longer valid so any
// PIDL walking routines will fail and the PIDL is orphaned
var
NS: TNamespace;
PIDL, NewPIDL: PItemIDList;
OldCB: Word;
begin
Result := False;
if PIDLMgr.IDCount(AbsolutePIDL) > 1 then
begin
PIDL := PIDLMgr.NextID(FAbsolutePIDL);
PIDL := PIDLMgr.NextID(PIDL); // Now we have the Drive
PIDL := PIDLMgr.NextID(PIDL); // Now we have the one past the Drive
OldCb := PIDL.mkid.cb;
PIDL.mkid.cb := 0;
NewPIDL := PIDLMgr.CopyPIDL(FAbsolutePIDL);
PIDL.mkid.cb := OldCB;
// NS is now a TNamespace to the Drive
NS := TNamespace.Create(NewPIDL, nil);
Result := NS.Removable;
NS.Free
end
end;
function TNamespace.IsControlPanel: Boolean;
begin
if Assigned(ControlPanelFolder) then
Result := ILIsEqual(AbsolutePIDL, ControlPanelFolder.AbsolutePIDL)
else
Result := False
end;
function TNamespace.IsControlPanelChildFolder: Boolean;
begin
if Assigned(ControlPanelFolder) then
Result := ILIsParent(ControlPanelFolder.AbsolutePIDL, AbsolutePIDL, True)
else
Result := False
end;
function TNamespace.IsDesktop: Boolean;
begin
Result := PIDLMgr.IsDesktopFolder(AbsolutePIDL)
end;
function TNamespace.IsMyComputer: Boolean;
begin
if Assigned(DrivesFolder) then
Result := ILIsEqual(DrivesFolder.AbsolutePIDL, AbsolutePIDL)
else
Result := False;
end;
function TNamespace.IsNetworkNeighborhood: Boolean;
begin
if Assigned(NetworkNeighborHoodFolder) then
Result := ILIsEqual(NetworkNeighborHoodFolder.AbsolutePIDL, AbsolutePIDL)
else
Result := False;
end;
function TNamespace.IsNetworkNeighborhoodChild: Boolean;
begin
if Assigned(NetworkNeighborHoodFolder) then
Result := ILIsParent(NetworkNeighborHoodFolder.AbsolutePIDL, AbsolutePIDL, False)
else
Result := False;
end;
function TNamespace.IsParentByNamespace(TestNamespace: TNamespace;
Immediate: Boolean): Boolean;
{ Returns True if the TestNamespace is a parent of the namespace. Immediate }
{ forces function to be true only of the passed PIDL is the immidiate parent }
{ of the namespace. }
begin
Result := Boolean( ILIsParent(TestNamespace.FAbsolutePIDL, FAbsolutePIDL, Immediate));
end;
function TNamespace.IsParentByPIDL(TestPIDL: PItemIDList;
Immediate: Boolean): Boolean;
{ Returns True if the TestPIDL is a parent of the namespace. Immediate forces }
{ function to be true only of the passed PIDL is the immidiate parent of the }
{ namespace. }
begin
Result := Boolean( ILIsParent(TestPIDL, FAbsolutePIDL, Immediate));
end;
function TNamespace.IsRecycleBin: Boolean;
begin
{ RecycleBin may not be avaiable if System Administrator has removed it in Win2k at least }
if Assigned(RecycleBinFolder) and not (nsRecycleBinChecked in States) then
begin
if ILIsEqual(AbsolutePIDL, RecycleBinFolder.AbsolutePIDL) then
Include(FStates, nsIsRecycleBin)
else
Exclude(FStates, nsIsRecycleBin);
Include(FStates, nsRecycleBinChecked);
end;
Result := nsIsRecycleBin in States;
end;
function TNamespace.ParseDisplayName: PItemIDList;
begin
Result := ParseDisplayName(NameForParsing)
end;
function TNamespace.ParseDisplayName(Path: WideString): PItemIDList;
var
chEaten: ULONG;
Attrib: ULONG;
Desktop: IShellFolder;
begin
Result := nil;
Attrib := 0;
SHGetDesktopFolder(Desktop);
if Assigned(Desktop) then
begin
if Desktop.ParseDisplayName(ParentWnd, nil, PWideChar( Path),
chEaten, Result, Attrib) <> NOERROR
then
Result := nil;
end
end;
function TNamespace.Paste(NamespaceArray: TNamespaceArray; AsShortCut: Boolean = False): Boolean;
var
NSA: TNamespaceArray;
i: integer;
begin
Result := False;
if CanPasteToAll(NamespaceArray) then
begin
if IsDesktop then
begin
SetLength(NSA, Length(NamespaceArray));
// Convert the virtual Desktop based TNamespaces to the Physical Desktop Folder based TNamespaces
for i := 0 to Length(NSA) - 1 do
NSA[i] := TNamespace.Create(PathToPIDL(NamespaceArray[i].NameForParsing), nil);
Result := PhysicalDesktopFolder.Paste(NSA, AsShortCut);
end else
begin
if VerifyPIDLRelationship(NamespaceArray, True) then
begin
if AsShortCut then
Result := ExecuteContextMenuVerb('pastelink', NamespaceToRelativePIDLArray(NamespaceArray))
else
Result := ExecuteContextMenuVerb('paste', NamespaceToRelativePIDLArray(NamespaceArray))
end else
begin
if AsShortCut then
ExecuteContextMenuVerbMultiPath('pastelink', NamespaceArray)
else
ExecuteContextMenuVerbMultiPath('paste', NamespaceArray)
end
end
end
end;
procedure TNamespace.LoadCategoryInfo;
var
ColumnID: TSHColumnID;
CatGUID: TGUID;
Buffer: WideString;
i: Integer;
Done: Boolean;
begin
if (CatInfo.CategoryCount = 0) and Assigned(ShellFolder2) and Assigned(CategoryProviderInterface) then
begin
CatGUID := GUID_NULL;
FCatInfo.CategoryCount := DetailsSupportedColumns;
SetLength(FCatInfo.Categories, DetailsSupportedColumns);
SetLength(FCatInfo.CategoryNames, DetailsSupportedColumns);
SetLength(FCatInfo.CatGUID, DetailsSupportedColumns);
SetLength(FCatInfo.ColumnID, DetailsSupportedColumns);
SetLength(FCatInfo.CanCatatorize, DetailsSupportedColumns);
SetLength(Buffer, 256);
for i := 0 to DetailsSupportedColumns - 1 do
begin
FillChar(ColumnID, SizeOf(ColumnID), #0);
FillChar(PWideChar( Buffer)^, 256 * 2, #0);
CatGUID := GUID_NULL;
FCatInfo.Categories[i].Description := '';
FCatInfo.Categories[i].Collapsed := False;
FCatInfo.Categories[i].Hidden := False;
FCatInfo.CategoryNames[i] := '';
FCatInfo.ColumnID[i].pid := 0;
FCatInfo.ColumnID[i].fmtid := GUID_NULL;
FCatInfo.CatGUID[i] := GUID_NULL;
FCatInfo.CanCatatorize[i] := False;
FCatInfo.DefaultColumn := -1;
if Succeeded(ShellFolder2.MapColumnToSCID(i, ColumnID)) then
begin
CatInfo.ColumnID[i] := ColumnID;
if Succeeded(CategoryProviderInterface.CanCategorizeOnSCID(ColumnID)) then
begin
ColumnID := CatInfo.ColumnID[i];
CatInfo.CanCatatorize[i] := True;
if Succeeded(CategoryProviderInterface.GetCategoryForSCID(ColumnID, CatGUID)) then
begin
ColumnID := CatInfo.ColumnID[i];
CatInfo.CatGUID[i] := CatGUID;
if not IsEqualGUID(CatGUID, GUID_NULL) then
if Succeeded(CategoryProviderInterface.GetCategoryName(CatGUID, PWideChar(Buffer), 256)) then
begin
FCatInfo.CategoryNames[i] := Buffer;
SetLength(FCatInfo.CategoryNames[i], lstrlenW(PWideChar(FCatInfo.CategoryNames[i])));
end
end
end
end
end;
CatGUID := GUID_NULL;
if Succeeded(CategoryProviderInterface.GetDefaultCategory(CatGUID, ColumnID)) then
begin
i := 0;
Done := False;
while (i < Length(CatInfo.ColumnID)) and not Done do
begin
if IsEqualGUID(ColumnID.fmtid, CatInfo.ColumnID[i].fmtid) and (ColumnID.pid = CatInfo.ColumnID[i].pid) then
begin
FCatInfo.DefaultColumn := i;
Done := True
end;
Inc(i)
end
end
end
end;
procedure TNamespace.SetDetailByThread(ColumnIndex: Integer; Detail: WideString);
var
TempCache: PDetailsOfCacheRec;
begin
EnsureDetailCache;
if DetailsValidIndex(ColumnIndex) then
begin
TempCache := @ShellCache.Data.DetailsOfCache[ColumnIndex];
TempCache.Caption := Detail;
Include(TempCache.Cached, docCaptionValid);
Include(TempCache.Cached, docThreadLoaded);
Exclude(TempCache.Cached, docThreadLoading);
end
end;
procedure TNamespace.SetFreePIDLOnDestroy(const Value: Boolean);
begin
if Value then
Include(FStates, nsFreePIDLOnDestroy)
else
Exclude(FStates, nsFreePIDLOnDestroy)
end;
procedure TNamespace.SetIconIndexChanged(const Value: Boolean);
// Sets or resets if the index changed. Currently the SetIconIndexByThread method sets
// this flag. It is not reset automaticlly it is up to the application to reset then
// when it has detected and used it.
begin
if Value then
Include(FStates, nsIconIndexChanged)
else
Exclude(FStates, nsIconIndexChanged);
end;
procedure TNamespace.SetIconIndexByThread(IconIndex: Integer; ClearThreadLoading: Boolean);
begin
Include(FStates, nsThreadedIconLoaded); // Small Normal Icon is now Cached
FShellCache.Data.SmallIcon := IconIndex;
Include(FShellCache.ShellCacheFlags, scSmallIcon);
if ClearThreadLoading then
Exclude(FStates, nsThreadedIconLoading);
IconIndexChanged := True;
end;
procedure TNamespace.SetImageByThread(Bitmap: TBitmap;
ClearThreadLoading: Boolean);
begin
Include(FStates, nsThreadedImageLoaded);
FImage := Bitmap;
if ClearThreadLoading then
Exclude(FStates, nsThreadedImageLoading);
end;
function TNamespace.SetNameOf(NewName: WideString): Boolean;
const
ALL_FOLDERS = SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN;
var
P, NewPIDL, NewAbsPIDL: PItemIDList;
Oldcb: Word;
OldCursor: TCursor;
begin
Result := False;
if CanRename and Assigned(ParentShellFolder) then
begin
OldCursor := Screen.Cursor;
Screen.Cursor := crHourglass;
try
{ The shell frees the PIDL so we need a copy }
P := PIDLMgr.CopyPIDL(FRelativePIDL);
NewPIDL := nil;
if Succeeded(ParentShellFolder.SetNameOf(ParentWnd, P, PWideChar(NewName), ALL_FOLDERS, NewPIDL)) then
begin
// Win98 will return success but never touch NewPIDL when trying to change name
// of dialup connection. Not sure how do it if this fails though??
if Assigned(NewPIDL) then
begin
Result := True;
{ Temporary shortening of AbsolutePIDL }
Oldcb := RelativePIDL.mkid.cb;
RelativePIDL.mkid.cb := 0;
NewAbsPIDL := PIDLMgr.AppendPIDL(AbsolutePIDL, NewPIDL);
RelativePIDL.mkid.cb := Oldcb;
PIDLMgr.FreePIDL(FAbsolutePIDL); // Remember Relative PIDL overlays AbsPIDL
FAbsolutePIDL := NewAbsPIDL;
FRelativePIDL := PIDLMgr.GetPointerToLastID(AbsolutePIDL);
end
end
finally
Screen.Cursor := OldCursor
end
end;
end;
procedure TNamespace.SetThreadedDetailLoaded(ColumnIndex: Integer; Value: Boolean);
begin
EnsureDetailCache;
if DetailsValidIndex(ColumnIndex) then
Include(ShellCache.Data.DetailsOfCache[ColumnIndex].Cached, docThreadLoaded)
end;
procedure TNamespace.SetThreadedDetailLoading(ColumnIndex: Integer; Value: Boolean);
begin
EnsureDetailCache;
if DetailsValidIndex(ColumnIndex) then
Include(ShellCache.Data.DetailsOfCache[ColumnIndex].Cached, docThreadLoading)
end;
procedure TNamespace.SetThreadIconLoading(const Value: Boolean);
begin
if Value then
Include(FStates, nsThreadedIconLoading)
else
Exclude(FStates, nsThreadedIconLoading)
end;
function TNamespace.GetThreadedImageLoaded: Boolean;
begin
Result := nsThreadedImageLoaded in States
end;
function TNamespace.GetThreadedImageLoading: Boolean;
begin
Result := nsThreadedImageLoading in States
end;
procedure TNamespace.SetThreadImageLoading(const Value: Boolean);
begin
if Value then
Include(FStates, nsThreadedImageLoading)
else
Exclude(FStates, nsThreadedImageLoading)
end;
function TNamespace.ShellExecuteNamespace(WorkingDir, CmdLineArguments: WideString;
ExecuteFolder: Boolean = False; ExecuteFolderShortCut: Boolean = False;
RunInThread: Boolean = False): Boolean;
{ Attempts execute the object that the namespace is representing. WorkingDir }
{ is the directory that will be the current directory of the application that }
{ is being executed. If the directory does not exist the directory where the }
{ file being executed resides. CmdLineArguments are any switches or parameters }
{ that can be added to the file being executed. }
{ ExecuteFolder stops the call from being performed if the namespace is a folder}
{ Doing so usually opens an explorer window to Explore the folder. }
var
ShellExecuteInfoA: TShellExecuteInfoA;
ShellExecuteInfoW: TShellExecuteInfoW;
ShortWorkingDir, ShortCmdLine: string;
DoExecute: Boolean;
ShellLink: TVirtualShellLink;
ShellExecuteThread: TCommonShellExecuteThread;
begin
Result := False;
DoExecute := True;
if not ExecuteFolder then
DoExecute := not Folder;
if not ExecuteFolderShortCut and DoExecute then
begin
if Link then
begin
ShellLink := TVirtualShellLink.Create(nil);
try
ShellLink.ReadLink(NameParseAddress);
DoExecute := not WideDirectoryExists(ShellLink.TargetPath);
finally
ShellLink.Free
end
end
end;
if DoExecute then
begin
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin
FillChar(ShellExecuteInfoW, SizeOf(TShellExecuteInfoW), #0);
if WideDirectoryExists(WorkingDir) then
ShellExecuteInfoW.lpDirectory := PWideChar(WorkingDir)
else // This should always be a file not a folder so this is ok
ShellExecuteInfoW.lpDirectory := PWideChar(WideExtractFileDir(NameParseAddress));
ShellExecuteInfoW.cbSize := SizeOf(TShellExecuteInfoW);
ShellExecuteInfoW.fMask := SEE_MASK_INVOKEIDLIST or SEE_MASK_NOCLOSEPROCESS;
if RunInThread then
ShellExecuteInfoW.fMask := ShellExecuteInfoW.fMask or SEE_MASK_FLAG_DDEWAIT;
ShellExecuteInfoW.Wnd:= ParentWnd;
ShellExecuteInfoW.nShow := SW_SHOWNORMAL;
ShellExecuteInfoW.lpIDList:= AbsolutePIDL;
ShellExecuteInfoW.lpParameters := PWideChar(CmdLineArguments);
if RunInThread then
begin
ShellExecuteThread := TCommonShellExecuteThread.Create(True);
ShellExecuteThread.ShellExecuteInfoW.cbSize := ShellExecuteInfoW.cbSize;
ShellExecuteThread.ShellExecuteInfoW.fMask := ShellExecuteInfoW.fMask;
ShellExecuteThread.ShellExecuteInfoW.Wnd := ShellExecuteInfoW.Wnd;
ShellExecuteThread.ShellExecuteInfoW.nShow := ShellExecuteInfoW.nShow;
ShellExecuteThread.ShellExecuteInfoW.hInstApp := ShellExecuteInfoW.hInstApp;
ShellExecuteThread.ShellExecuteInfoW.hkeyClass := ShellExecuteInfoW.hkeyClass;
ShellExecuteThread.ShellExecuteInfoW.dwHotKey := ShellExecuteInfoW.dwHotKey;
ShellExecuteThread.ShellExecuteInfoW.hIcon := ShellExecuteInfoW.hIcon;
ShellExecuteThread.ShellExecuteInfoW.hProcess := ShellExecuteInfoW.hProcess;
ShellExecuteThread.lpDirectory := ShellExecuteInfoW.lpDirectory;
ShellExecuteThread.lpParameters := ShellExecuteInfoW.lpParameters;
ShellExecuteThread.PIDL := PIDLMgr.CopyPIDL(ShellExecuteInfoW.lpIDList);
ShellExecuteThread.Resume;
Result := True;
end else
Result := ShellExecuteExW_MP(@ShellExecuteInfoW);
end else
begin
FillChar(ShellExecuteInfoA, SizeOf(TShellExecuteInfo), #0);
if WideDirectoryExists(WorkingDir) then
ShortWorkingDir := WorkingDir
else
ShortWorkingDir := ExtractFileDir(NameParseAddress);
ShellExecuteInfoA.lpDirectory := PChar(ShortWorkingDir);
ShellExecuteInfoA.cbSize := SizeOf(TShellExecuteInfo);
ShellExecuteInfoA.fMask := SEE_MASK_INVOKEIDLIST or SEE_MASK_NOCLOSEPROCESS;
if RunInThread then
ShellExecuteInfoA.fMask := ShellExecuteInfoA.fMask or SEE_MASK_FLAG_DDEWAIT;
ShellExecuteInfoA.Wnd:= ParentWnd;
ShellExecuteInfoA.nShow := SW_SHOWNORMAL;
ShellExecuteInfoA.lpIDList:= AbsolutePIDL;
ShortCmdLine := CmdLineArguments;
ShellExecuteInfoA.lpParameters := PChar(ShortCmdLine);
if RunInThread then
begin
ShellExecuteThread := TCommonShellExecuteThread.Create(True);
ShellExecuteThread.ShellExecuteInfoA.cbSize := ShellExecuteInfoA.cbSize;
ShellExecuteThread.ShellExecuteInfoA.fMask := ShellExecuteInfoA.fMask;
ShellExecuteThread.ShellExecuteInfoA.Wnd := ShellExecuteInfoA.Wnd;
ShellExecuteThread.ShellExecuteInfoA.nShow := ShellExecuteInfoA.nShow;
ShellExecuteThread.ShellExecuteInfoA.hInstApp := ShellExecuteInfoA.hInstApp;
ShellExecuteThread.ShellExecuteInfoA.hkeyClass := ShellExecuteInfoA.hkeyClass;
ShellExecuteThread.ShellExecuteInfoA.dwHotKey := ShellExecuteInfoA.dwHotKey;
ShellExecuteThread.ShellExecuteInfoA.hIcon := ShellExecuteInfoA.hIcon;
ShellExecuteThread.ShellExecuteInfoA.hProcess := ShellExecuteInfoA.hProcess;
ShellExecuteThread.lpDirectory := ShellExecuteInfoA.lpDirectory;
ShellExecuteThread.lpParameters := ShellExecuteInfoA.lpParameters;
ShellExecuteThread.PIDL := PIDLMgr.CopyPIDL(ShellExecuteInfoA.lpIDList);
ShellExecuteThread.Resume;
Result := True;
end else
Result := ShellExecuteEx(@ShellExecuteInfoA);
end
end
end;
function TNamespace.ShowContextMenu(Owner: TWinControl;
ContextMenuCmdCallback: TContextMenuCmdCallback;
ContextMenuShowCallback: TContextMenuShowCallback;
ContextMenuAfterCmdCallback: TContextMenuAfterCmdCallback;
Position: PPoint = nil;
CustomShellSubMenu: TPopupMenu = nil;
CustomSubMenuCaption: WideString = ''): Boolean;
{ Displays the ContextMenu of the namespace. }
var
PIDLArray: TRelativePIDLArray;
begin
SetLength(PIDLArray, 1);
PIDLArray[0] := RelativePIDL;
Result := InternalShowContextMenu(Owner, ContextMenuCmdCallback, ContextMenuShowCallback,
ContextMenuAfterCmdCallback, PIDLArray, Position, CustomShellSubMenu, CustomSubMenuCaption);
end;
function TNamespace.ShowContextMenuMulti(Owner: TWinControl;
ContextMenuCmdCallback: TContextMenuCmdCallback;
ContextMenuShowCallback: TContextMenuShowCallback;
ContextMenuAfterCmdCallback: TContextMenuAfterCmdCallback;
NamespaceArray: TNamespaceArray; Position: PPoint = nil;
CustomShellSubMenu: TPopupMenu = nil; CustomSubMenuCaption: WideString = ''): Boolean;
begin
Result := False;
if VerifyPIDLRelationship(NamespaceArray, True) then
begin
Result := InternalShowContextMenu(Owner, ContextMenuCmdCallBack,
ContextMenuShowCallback, ContextMenuAfterCmdCallback,
NamespaceToRelativePIDLArray(NamespaceArray), Position, CustomShellSubMenu,
CustomSubMenuCaption)
end else
begin
if Length(NamespaceArray) > 0 then
ShowContextMenuMultiPath(Owner, NamespaceArray[0], NamespaceArray, Position)
end
end;
function TNamespace.ShowContextMenuMultiPath(Owner: TWinControl;
Focused: TNamespace; Namespaces: TNamespaceArray; Position: PPoint = nil): Boolean;
var
Menu: TCommonShellMultiParentContextMenu;
Temp: TWinControl;
begin
Menu := TCommonShellMultiParentContextMenu.Create(nil);
Temp := TWinControl.CreateParented(GetDesktopWindow);
try
Temp.Width := 0;
Temp.Height := 0;
Temp.Visible := True;
Result := Menu.ShowContextMenu(Owner, Focused, Namespaces, Position);
Temp.Visible := False;
finally
Temp.Free;
Menu.Free
end
end;
procedure TNamespace.ShowPropertySheet;
var
NamespaceArray: TNamespaceArray;
begin
if HasPropSheet then
begin
SetLength(NamespaceArray, 1);
NamespaceArray[0] := Self;
if VerifyPIDLRelationship(NamespaceArray, True) then
ExecuteContextMenuVerb('properties', NamespaceToRelativePIDLArray(NamespaceArray))
else
ExecuteContextMenuVerbMultiPath('properties', NamespaceArray)
end
end;
procedure TNamespace.ShowPropertySheetMulti(NamespaceArray: TNamespaceArray;
UseSHMultiFileProperties: Boolean = True; ForceNonMultiPath: Boolean = False);
var
IDO: IDataObject;
begin
if CanShowPropertiesOfAll(NamespaceArray) then
begin
// Call SHMultiFileProperties_MP to show the property sheet when the
// APIDLArray items are from different folders.
// Minimum OS: Win2k
if not ForceNonMultiPath and (UseSHMultiFileProperties and Assigned(SHMultiFileProperties_MP)) then
begin
CreateFullyQualifiedShellDataObject(NamespaceToAbsolutePIDLArray(NamespaceArray), IDO);
if Assigned(IDO) then
SHMultiFileProperties_MP(IDO, 0);
IDO._Release // I did this in DefMenuCreateCallback, I don't know why I have to but I do
end else
begin
if VerifyPIDLRelationship(NamespaceArray, True) or ForceNonMultiPath then
ExecuteContextMenuVerb('properties', NamespaceToRelativePIDLArray(NamespaceArray))
else
ExecuteContextMenuVerbMultiPath('properties', NamespaceArray)
end
end
end;
function TNamespace.TestAttributesOf(Flags: Longword; FlushCache: Boolean; SoftFlush: Boolean = False): Boolean;
// Pass any of the flags for IShellFolder.GetAttributesOf to see if they exist
// for the Folder. FlushCache forces the shell to reload the information on the
// namespace. Useful to handle the bug where the shell caches the icon for a
// CD drive and never changes it. Flushing it will force it to reload the Index
// Note this is dangerous with 3rd party namespaces. M$ suggests this method for
// their namespaces but at least Hummingbird network namespace crashes with this
// they apparently don't check for 0 PIDL's
// Soft Flush add the SFGAO_VALIDATE flag to get the fresh info
var
x: Longword;
begin
if Assigned(ParentShellFolder) then
begin
x := Flags;
if FlushCache then
begin
x := x or SFGAO_VALIDATE;
ParentShellFolder.GetAttributesOf(0, FRelativePIDL, x);
end else
if SoftFlush then
begin
x := x or SFGAO_VALIDATE;
ParentShellFolder.GetAttributesOf(1, FRelativePIDL, x)
end else
ParentShellFolder.GetAttributesOf(1, FRelativePIDL, x);
Result := Flags and x = Flags;
end else
Result := False;
end;
function TNamespace.VerifyPIDLRelationship(NamespaceArray: TNamespaceArray;
Silent: Boolean = False): Boolean;
var
i: integer;
begin
Result := True;
i := 0;
while (i < Length(NamespaceArray)) and Result do
begin
{ TNamespace is based off using the parent to access the data so it is }
{ correct to do the test for childPIDLs relative from the parent. }
if IsDesktop and (Length(NamespaceArray) = 1) then
begin
Result := (PIDLMgr.IDCount(NamespaceArray[i].AbsolutePIDL) = 1) or (NamespaceArray[0].IsDesktop)
end else
begin
if Assigned(Parent) then
Result := ILIsParent(Parent.AbsolutePIDL, NamespaceArray[i].AbsolutePIDL, True)
else begin
if (Length(NamespaceArray) = 1) and NamespaceArray[0].IsDesktop then
Result := True
else
Result := False;
end
end;
Inc(i)
end;
if not Silent and (not Result and not IsDesktop) then
WideShowMessage(ParentWnd, STR_ERROR, STR_ERR_BAD_PIDL_RELATIONSHIP);
end;
procedure TNamespace.WindowProcForContextMenu(var Message: TMessage);
begin
if Assigned(FOldWndProcForContextMenu) then
FOldWndProcForContextMenu(Message); // Call the OldWindProc of the ContextMenu owner
case Message.Msg of
WM_DRAWITEM, WM_INITMENUPOPUP, WM_MEASUREITEM, WM_MENUCHAR:
HandleContextMenuMsg(Message.Msg, Message.WParam, Message.LParam, Message.Result);
end;
end;
function TNamespace.GetCategoryProviderInterface: ICategoryProvider;
begin
if not Assigned(FCategoryProviderInterface) and Folder and Assigned(ShellFolder) then
begin
if not Succeeded(ShellFolder.CreateViewObject(ParentWnd, IID_ICategoryProvider, Pointer(FCategoryProviderInterface))) then
FCategoryProviderInterface := nil
end;
Result := FCategoryProviderInterface
end;
function TNamespace.CreateCategory(GUID: TGUID): ICategorizer;
begin
Result := nil;
if Assigned(CategoryProviderInterface) then
begin
if not Succeeded(CategoryProviderInterface.CreateCategory(GUID, IID_ICategorizer, Result)) then
Result := nil;
end
end;
function TNamespace.EnumFuncDummy(MessageWnd: HWnd; APIDL: PItemIDList; AParent: TNamespace; Data: Pointer; var Terminate: Boolean): Boolean;
begin
PIDLMgr.FreePIDL(APIDL);
Result := True;
end;
function TNamespace.GetBrowserFrameOptionsInterface: IBrowserFrameOptions;
var
Found: Boolean;
begin
if not Assigned(FBrowserFrameOptionsInterface) then
begin
Found := False;
if Assigned(Parent) then
begin
Found := Succeeded(Parent.ShellFolder.GetUIObjectOf(ParentWnd, 1, FRelativePIDL, IBrowserFrameOptions, nil, Pointer(FBrowserFrameOptionsInterface)));
if not Found and Folder then
begin
Found := Succeeded(ShellFolder.CreateViewObject(ParentWnd, IBrowserFrameOptions, Pointer(FBrowserFrameOptionsInterface)));
if not Found then
Found := Succeeded(ShellFolder.QueryInterface(IBrowserFrameOptions, Pointer(FBrowserFrameOptionsInterface)))
end
end;
if not Found then
FBrowserFrameOptionsInterface := nil
end;
Result := FBrowserFrameOptionsInterface
end;
function TNamespace.GetQueryAssociationsInterface: IQueryAssociations;
var
Found: Boolean;
begin
if not Assigned(FQueryAssociationsInterface) then
begin
Found := False;
if Assigned(Parent) then
begin
Found := Succeeded(Parent.ShellFolder.GetUIObjectOf(ParentWnd, 1, FRelativePIDL, IQueryAssociations, nil, Pointer(FQueryAssociationsInterface)));
if not Found and Folder then
begin
Found := Succeeded(ShellFolder.CreateViewObject(ParentWnd, IQueryAssociations, Pointer(FQueryAssociationsInterface)));
if not Found then
Found := Succeeded(ShellFolder.QueryInterface(IQueryAssociations, Pointer(FQueryAssociationsInterface)))
end
end;
if not Found then
FQueryAssociationsInterface := nil
end;
Result := FQueryAssociationsInterface
end;
function TNamespace.GetValid: Boolean;
var
rgfInOut: UINT;
begin
// Does not work on floppy drives and such
// password proctected network drives also return false regardless if they have
// not been logged into yet so return true for those too.
if (not Removable and (Assigned(ParentShellFolder))) and not PotentialMappedDrive(Self) then
begin
rgfInOut := SFGAO_VALIDATE;
// This returns false on a password protected network folder
Result := ParentShellFolder.GetAttributesOf(1, FRelativePIDL, rgfInOut) = NOERROR
end else
Result := True
end;
procedure TNamespace.ReplacePIDL(NewPIDL: PItemIDList; AParent: TNamespace);
begin
InvalidateNamespace(True);
PIDLMgr.FreeAndNilPIDL(FAbsolutePIDL);
if Assigned(Parent) then
FAbsolutePIDL := PIDLMgr.AppendPIDL(AParent.FAbsolutePIDL, PIDLMgr.CopyPIDL(NewPIDL))
else
FAbsolutePIDL := PIDLMgr.CopyPIDL(NewPIDL);
FRelativePIDL := PIDLMgr.GetPointerToLastID(FAbsolutePIDL);
end;
{ TExtractImage }
constructor TExtractImage.Create;
begin
FWidth := 200;
FHeight := 200;
FColorDepth := 32;
FFlags := IEIFLAG_SCREEN;
end;
function TExtractImage.GetImage: TBitmap;
function BitsToPixelFormat(Bits: Windows.TBITMAP): TPixelFormat;
begin
case Bits.bmBitsPixel of
32: Result := pf32Bit;
24: Result := pf24Bit;
16: Result := pf16Bit;
15: Result := pf15Bit;
8: Result := pf8Bit;
4: Result := pf4Bit;
1: Result := pf1Bit;
else
Result := pfDevice
end
end;
var
Bits: HBITMAP;
begin
Bits := 0;
Result := nil;
if Assigned(ExtractImageInterface) then
if Succeeded(ExtractImageInterface.Extract(Bits)) then
begin
// Can't just assign the bitmap handle to the canvas because the bitmap
// may not be a DIB. If not then if the bitmap is written to as stream then
// TBitmap will be created as a DIB when it is read back from the stream and
// it will be displayed upside down
Result := TBitmap.Create;
Result.PixelFormat := pf32Bit; //BitsToPixelFormat(BitInfo);
Result.Transparent := True;
Result.Handle := Bits;
end
end;
function TExtractImage.GetExtractImageInterface2: IExtractImage2;
var
Found: Boolean;
begin
if not Assigned(FExtractImage2Interface) then
begin
Found := False;
if Assigned(ExtractImageInterface) then
Found := ExtractImageInterface.QueryInterface(IID_IExtractImage2,
Pointer(FExtractImage2Interface)) <> E_NOINTERFACE;
if not Found then
FExtractImage2Interface := nil
end;
Result := FExtractImage2Interface
end;
function TExtractImage.GetExtractImageInterface: IExtractImage;
var
Found: Boolean;
begin
if not Assigned(FExtractImageInterface) then
begin
Found := False;
if Assigned(Owner.ParentShellFolder) then
begin
Found := Owner.ParentShellFolder.GetUIObjectOf(0, 1, Owner.FRelativePIDL,
IExtractImage, nil, Pointer(FExtractImageInterface)) = NOERROR;
end;
if not Found and Assigned(Owner.ShellFolder) then
begin
Found := Owner.ShellFolder.CreateViewObject(0, IExtractImage,
Pointer(FExtractImageInterface)) = NOERROR;
end;
if not Found then
FExtractImageInterface := nil
end;
Result := FExtractImageInterface
end;
function TExtractImage.GetImagePath: WideString;
var
Size: TSize;
Buffer: PWideChar;
begin
if Assigned(ExtractImageInterface) then
begin
GetMem(Buffer, MAX_PATH * 4);
try
try
Size.cx := Width;
Size.cy := Height;
if ExtractImageInterface.GetLocation(Buffer, MAX_PATH, FPriority, Size,
ColorDepth, FFlags) = NOERROR then
begin
Result := Buffer;
PathExtracted := True
end else
Result := '';
finally
FreeMem(Buffer);
end except
Result := ''
end
end;
end;
{ ----------------------------------------------------------------------------- }
{ Encapsulation of IShellLink }
{ ----------------------------------------------------------------------------- }
{ TVirtualShellLink }
destructor TVirtualShellLink.Destroy;
begin
FreeTargetIDList;
inherited;
end;
procedure TVirtualShellLink.FreeTargetIDList;
var
Malloc: IMalloc;
PIDL: PItemIDList;
begin
if Assigned(TargetIDList) then
begin
PIDL := TargetIDLIst;
TargetIDList := nil;
SHGetMalloc(Malloc);
Malloc.Free(PIDL);
end;
end;
function TVirtualShellLink.GetShellLinkAInterface: IShellLink;
begin
if not Assigned(FShellLinkA) then
begin
if not Succeeded(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,
IShellLinkA, FShellLinkA))
then
FShellLinkA := nil;
end;
Result := FShellLinkA
end;
function TVirtualShellLink.GetShellLinkWInterface: IShellLinkW;
begin
if not Assigned(FShellLinkW) then
begin
if not Succeeded(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,
IShellLinkW, FShellLinkW))
then
FShellLinkW := nil
end;
Result := FShellLinkW
end;
function TVirtualShellLink.ReadLink(LinkFileName: WideString): Boolean;
const
BUFFERSIZE = 1024;
var
Success: Boolean;
S: string;
PersistFile: IPersistFile;
pwHotKey: Word;
Cmd: integer;
FindData: WIN32_FIND_DATA;
FindDataW: WIN32_FIND_DATAW;
begin
Result := False;
Success := False;
if Assigned(ShellLinkWInterface) then
begin
if CommonSupports(ShellLinkWInterface, IPersistFile, PersistFile) then
begin
FFileName := LinkFileName;
Success := Succeeded(PersistFile.Load(PWideChar(FileName), STGM_READWRITE));
if Success then
begin
Result := True;
SetLength(FTargetPath, BUFFERSIZE);
Success := Succeeded(ShellLinkWInterface.GetPath(PWideChar( FTargetPath), MAX_PATH, FindDataW, SLGP_UNCPRIORITY));
if Success then
SetLength(FTargetPath, lstrlenW(PWideChar( FTargetPath)));
SetLength(FArguments, BUFFERSIZE);
Success := Succeeded(ShellLinkWInterface.GetArguments(PWideChar( FArguments), BUFFERSIZE));
if Success then
SetLength(FArguments, lstrlenW(PWideChar( FArguments)));
SetLength(FDescription, BUFFERSIZE);
Success := Succeeded(ShellLinkWInterface.GetDescription(PWideChar( FDescription), BUFFERSIZE));
if Success then
SetLength(FDescription, lstrlenW(PWideChar( FDescription)));
SetLength(FWorkingDirectory, BUFFERSIZE);
Success := Succeeded(ShellLinkWInterface.GetWorkingDirectory(PWideChar( FWorkingDirectory), BUFFERSIZE));
if Success then
SetLength(FWorkingDirectory, lstrlenW(PWideChar( FWorkingDirectory)));
SetLength(FIconLocation, BUFFERSIZE);
Success := Succeeded(ShellLinkWInterface.GetIconLocation(PWideChar( FIconLocation), BUFFERSIZE, FIconIndex));
if Success then
SetLength(FIconLocation, lstrlenW(PWideChar( FIconLocation)));
FreeTargetIDList;
ShellLinkWInterface.GetIDList(FTargetIDList);
Success := Succeeded(ShellLinkWInterface.GetHotKey(pwHotKey));
if Success then
begin
FHotKey := LoByte(pwHotKey);
FHotKeyModifiers := [];
if HiByte(pwHotKey) and HOTKEYF_ALT <> 0 then Include(FHotKeyModifiers, hkmAlt);
if HiByte(pwHotKey) and HOTKEYF_CONTROL <> 0 then Include(FHotKeyModifiers, hkmControl);
if HiByte(pwHotKey) and HOTKEYF_EXT <> 0 then Include(FHotKeyModifiers, hkmExtendedKey);
if HiByte(pwHotKey) and HOTKEYF_SHIFT <> 0 then Include(FHotKeyModifiers, hkmShift);
end;
Success := Succeeded(ShellLinkWInterface.GetShowCmd(Cmd));
if Success then
case Cmd of
SW_HIDE: ShowCmd := swHide;
SW_MAXIMIZE: ShowCmd := swMaximize;
SW_MINIMIZE: ShowCmd := swMinimize;
SW_RESTORE: ShowCmd := swRestore;
SW_SHOW: ShowCmd := swShow;
SW_SHOWDEFAULT: ShowCmd := swShowDefault;
SW_SHOWMINIMIZED: ShowCmd := swShowMinimized;
SW_SHOWMINNOACTIVE: ShowCmd := swShowMinNoActive;
SW_SHOWNA : ShowCmd := swShowNA;
SW_SHOWNOACTIVATE : ShowCmd := swShowNoActive;
SW_SHOWNORMAL: ShowCmd := swShowNormal;
end;
// Why was that here? Removed 11.12.02
// PersistFile.Save(PWideChar(FileName), True)
end else
FFileName := ''
end
end;
if not Success and Assigned(ShellLinkAInterface) then
begin
if CommonSupports(ShellLinkAInterface, IPersistFile, PersistFile) then
begin
FFileName := LinkFileName;
Success := Succeeded(PersistFile.Load(PWideChar(FileName), STGM_READWRITE));
if Success then
begin
Result := True;
SetLength(S, BUFFERSIZE);
Success := Succeeded(ShellLinkAInterface.GetPath(PChar( S), MAX_PATH, FindData, SLGP_UNCPRIORITY));
if Success then
begin
SetLength(S, lstrlen(PChar( S)));
FTargetPath := S
end;
SetLength(S, BUFFERSIZE);
Success := Succeeded(ShellLinkAInterface.GetArguments(PChar( S), BUFFERSIZE));
if Success then
begin
SetLength(S, lstrlen(PChar( S)));
FArguments := S
end;
SetLength(S, BUFFERSIZE);
Success := Succeeded(ShellLinkAInterface.GetDescription(PChar( S), BUFFERSIZE));
if Success then
begin
SetLength(S, lstrlen(PChar( S)));
FDescription := S
end;
SetLength(S, BUFFERSIZE);
Success := Succeeded(ShellLinkAInterface.GetWorkingDirectory(PChar( S), BUFFERSIZE));
if Success then
begin
SetLength(S, lstrlen(PChar( S)));
FWorkingDirectory := S
end;
SetLength(S, BUFFERSIZE);
Success := Succeeded(ShellLinkAInterface.GetIconLocation(PChar( S), BUFFERSIZE, FIconIndex));
if Success then
begin
SetLength(S, lstrlen(PChar( S)));
FIconLocation := S
end;
FreeTargetIDList;
ShellLinkAInterface.GetIDList(FTargetIDList);
Success := Succeeded(ShellLinkAInterface.GetHotKey(pwHotKey));
if Success then
begin
FHotKey := LoByte(pwHotKey);
FHotKeyModifiers := [];
if HiByte(pwHotKey) and HOTKEYF_ALT <> 0 then Include(FHotKeyModifiers, hkmAlt);
if HiByte(pwHotKey) and HOTKEYF_CONTROL <> 0 then Include(FHotKeyModifiers, hkmControl);
if HiByte(pwHotKey) and HOTKEYF_EXT <> 0 then Include(FHotKeyModifiers, hkmExtendedKey);
if HiByte(pwHotKey) and HOTKEYF_SHIFT <> 0 then Include(FHotKeyModifiers, hkmShift);
end;
Success := Succeeded(ShellLinkAInterface.GetShowCmd(Cmd));
if Success then
case Cmd of
SW_HIDE: ShowCmd := swHide;
SW_MAXIMIZE: ShowCmd := swMaximize;
SW_MINIMIZE: ShowCmd := swMinimize;
SW_RESTORE: ShowCmd := swRestore;
SW_SHOW: ShowCmd := swShow;
SW_SHOWDEFAULT: ShowCmd := swShowDefault;
SW_SHOWMINIMIZED: ShowCmd := swShowMinimized;
SW_SHOWMINNOACTIVE: ShowCmd := swShowMinNoActive;
SW_SHOWNA : ShowCmd := swShowNA;
SW_SHOWNOACTIVATE : ShowCmd := swShowNoActive;
SW_SHOWNORMAL: ShowCmd := swShowNormal;
end;
PersistFile.Save(PWideChar(FileName), True)
end else
FFileName := '';
end
end
end;
function TVirtualShellLink.WriteLink(LinkFileName: WideString): Boolean;
var
S: string;
PersistFile: IPersistFile;
pwHotKey, pwHotKeyHi: Word;
KeyModifier: THotKeyModifiers;
Cmd: integer;
begin
Result := False;
if (TargetPath = '') and not Assigned(TargetIDList) and not SilentWrite then
WideShowMessage(Application.Handle, STR_NOTARGETDEFINED, STR_ERROR)
else begin
if Assigned(ShellLinkWInterface) then
begin
if CommonSupports(ShellLinkWInterface, IPersistFile, PersistFile) then
begin
FFileName := LinkFileName;
ShellLinkWInterface.SetPath(PWideChar( FTargetPath));
ShellLinkWInterface.SetArguments(PWideChar( FArguments));
ShellLinkWInterface.SetDescription(PWideChar( FDescription));
ShellLinkWInterface.SetPath(PWideChar( FTargetPath));
ShellLinkWInterface.SetWorkingDirectory(PWideChar( FWorkingDirectory));
ShellLinkWInterface.SetIconLocation(PWideChar( FIconLocation), FIconIndex);
if Assigned(FTargetIDList) then
ShellLinkWInterface.SetIDList(FTargetIDList);
pwHotKey := HotKey;
pwHotKeyHi := 0;
KeyModifier := HotKeyModifiers;
if hkmAlt in KeyModifier then pwHotKeyHi := pwHotKeyHi or HOTKEYF_ALT;
if hkmControl in KeyModifier then pwHotKeyHi := pwHotKeyHi or HOTKEYF_CONTROL;
if hkmExtendedKey in KeyModifier then pwHotKeyHi := pwHotKeyHi or HOTKEYF_EXT;
if hkmShift in KeyModifier then pwHotKeyHi := pwHotKeyHi or HOTKEYF_SHIFT;
pwHotKeyHi := pwHotKeyHi shl 8; // Make lower 8 bits the upper 8 bits
pwHotKeyHi := pwHotKeyHi and $FF00; // Make sure lower 8 bits clear
pwHotKey := pwHotKey or pwHotKeyHi;
ShellLinkWInterface.SetHotkey(pwHotKey);
case ShowCmd of
swHide: Cmd := SW_HIDE;
swMaximize: Cmd := SW_MAXIMIZE;
swMinimize: Cmd := SW_MINIMIZE;
swRestore: Cmd := SW_RESTORE;
swShow: Cmd := SW_SHOW;
swShowDefault: Cmd := SW_SHOWDEFAULT;
swShowMinimized: Cmd := SW_SHOWMINIMIZED;
swShowMinNoActive: Cmd := SW_SHOWMINNOACTIVE;
swShowNA: Cmd := SW_SHOWNA;
swShowNoActive: Cmd := SW_SHOWNOACTIVATE;
swShowNormal: Cmd := SW_SHOWNORMAL;
else
Cmd := SW_SHOWNORMAL
end;
ShellLinkWInterface.SetShowCmd(Cmd);
Result := Succeeded(PersistFile.Save(PWideChar(FileName), True))
end;
end;
if not Result and Assigned(ShellLinkAInterface) then
begin
if CommonSupports(ShellLinkAInterface, IPersistFile, PersistFile) then
begin
FFileName := LinkFileName;
S := FTargetPath;
ShellLinkAInterface.SetPath(PChar( S));
S := FArguments;
ShellLinkAInterface.SetArguments(PChar(S));
S := FDescription;
ShellLinkAInterface.SetDescription(PChar( S));
S := FTargetPath;
ShellLinkAInterface.SetPath(PChar( S));
S := FWorkingDirectory;
ShellLinkAInterface.SetWorkingDirectory(PChar( S));
S := FIconLocation;
ShellLinkAInterface.SetIconLocation(PChar( S), FIconIndex);
if Assigned(FTargetIDList) then
ShellLinkAInterface.SetIDList(FTargetIDList);
pwHotKey := HotKey;
pwHotKeyHi := 0;
KeyModifier := HotKeyModifiers;
if hkmAlt in KeyModifier then pwHotKeyHi := pwHotKeyHi or HOTKEYF_ALT;
if hkmControl in KeyModifier then pwHotKeyHi := pwHotKeyHi or HOTKEYF_CONTROL;
if hkmExtendedKey in KeyModifier then pwHotKeyHi := pwHotKeyHi or HOTKEYF_EXT;
if hkmShift in KeyModifier then pwHotKeyHi := pwHotKeyHi or HOTKEYF_SHIFT;
pwHotKeyHi := pwHotKeyHi shl 8; // Make lower 8 bits the upper 8 bits
pwHotKeyHi := pwHotKeyHi and $FF00; // Make sure lower 8 bits clear
pwHotKey := pwHotKey or pwHotKeyHi;
ShellLinkAInterface.SetHotkey(pwHotKey);
case ShowCmd of
swHide: Cmd := SW_HIDE;
swMaximize: Cmd := SW_MAXIMIZE;
swMinimize: Cmd := SW_MINIMIZE;
swRestore: Cmd := SW_RESTORE;
swShow: Cmd := SW_SHOW;
swShowDefault: Cmd := SW_SHOWDEFAULT;
swShowMinimized: Cmd := SW_SHOWMINIMIZED;
swShowMinNoActive: Cmd := SW_SHOWMINNOACTIVE;
swShowNA: Cmd := SW_SHOWNA;
swShowNoActive: Cmd := SW_SHOWNOACTIVATE;
swShowNormal: Cmd := SW_SHOWNORMAL;
else
Cmd := SW_SHOWNORMAL
end;
ShellLinkAInterface.SetShowCmd(Cmd);
Result := Succeeded(PersistFile.Save(PWideChar(FileName), True))
end
end
end
end;
{ ----------------------------------------------------------------------------- }
{ TList that implements basic streaming }
{ ----------------------------------------------------------------------------- }
{ TStreamableList }
constructor TStreamableList.Create;
begin
FStreamVersion := STREAM_VERSION_DEFAULT
end;
procedure TStreamableList.LoadFromFile(FileName: WideString; Version: integer = 0;
ReadVerFromStream: Boolean = False);
var
{$IFDEF TNTSUPPORT}
FileStream: TTntFileStream;
{$ELSE}
FileStream: TFileStream;
{$ENDIF}
begin
FileStream := nil;
try
{$IFDEF TNTSUPPORT}
FileStream := TTntFileStream.Create(FileName, fmOpenRead or fmShareExclusive);
{$ELSE}
FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareExclusive);
{$ENDIF}
LoadFromStream(FileStream);
finally
FileStream.Free
end;
end;
procedure TStreamableList.LoadFromStream(S: TStream; Version: integer;
ReadVerFromStream: Boolean);
begin
Clear;
if ReadVerFromStream then
S.ReadBuffer(FStreamVersion, Sizeof(FStreamVersion))
else
FStreamVersion := Version;
end;
procedure TStreamableList.SaveToFile(FileName: WideString; Version: integer = 0;
ReadVerFromStream: Boolean = False);
var
{$IFDEF TNTSUPPORT}
FileStream: TTntFileStream;
{$ELSE}
FileStream: TFileStream;
{$ENDIF}
begin
FileStream := nil;
try
{$IFDEF TNTSUPPORT}
FileStream := TTntFileStream.Create(FileName, fmCreate or fmShareExclusive);
{$ELSE}
FileStream := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
{$ENDIF}
SaveToStream(FileStream);
finally
FileStream.Free
end;
end;
procedure TStreamableList.SaveToStream(S: TStream; Version: integer;
WriteVerToStream: Boolean);
begin
if WriteVerToStream then
S.WriteBuffer(Version, Sizeof(Version));
FStreamVersion := Version;
end;
{ ----------------------------------------------------------------------------- }
{ TClass that implements basic streaming }
{ ----------------------------------------------------------------------------- }
{ TStreamableClass }
constructor TStreamableClass.Create;
begin
FStreamVersion := STREAM_VERSION_DEFAULT
end;
procedure TStreamableClass.LoadFromFile(FileName: WideString; Version: integer = 0; ReadVerFromStream: Boolean = False);
var
{$IFDEF TNTSUPPORT}
FileStream: TTntFileStream;
{$ELSE}
FileStream: TFileStream;
{$ENDIF}
begin
FileStream := nil;
try
{$IFDEF TNTSUPPORT}
FileStream := TTntFileStream.Create(FileName, fmOpenRead or fmShareExclusive);
{$ELSE}
FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareExclusive);
{$ENDIF}
LoadFromStream(FileStream, Version, ReadVerFromStream);
finally
FileStream.Free
end;
end;
procedure TStreamableClass.LoadFromStream(S: TStream; Version: integer;
ReadVerFromStream: Boolean);
begin
if ReadVerFromStream then
S.ReadBuffer(FStreamVersion, Sizeof(FStreamVersion))
else
FStreamVersion := Version;
end;
procedure TStreamableClass.SaveToFile(FileName: WideString; Version: integer = 0; ReadVerFromStream: Boolean = False);
var
{$IFDEF TNTSUPPORT}
FileStream: TTntFileStream;
{$ELSE}
FileStream: TFileStream;
{$ENDIF}
begin
FileStream := nil;
try
{$IFDEF TNTSUPPORT}
FileStream := TTntFileStream.Create(FileName, fmCreate or fmShareExclusive);
{$ELSE}
FileStream := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
{$ENDIF}
SaveToStream(FileStream, Version, ReadVerFromStream);
finally
FileStream.Free
end;
end;
procedure TStreamableClass.SaveToStream(S: TStream; Version: integer;
WriteVerToStream: Boolean);
begin
if WriteVerToStream then
S.WriteBuffer(Version, Sizeof(Version));
FStreamVersion := Version;
end;
{ ----------------------------------------------------------------------------- }
{ Class that frees it self when the reference count goes to 0. Like a com }
{ object but the compiler does not inc/dec automaticlly }
{ ----------------------------------------------------------------------------- }
{ TReferenceCounted }
procedure TReferenceCounted.AddRef;
begin
InterlockedIncrement(FRefCount)
end;
procedure TReferenceCounted.Release;
begin
InterlockedDecrement (FRefCount);
if FRefCount <= 0 then
Free;
end;
{ ----------------------------------------------------------------------------- }
{ TList that frees it self when the reference count goes to 0. Like a com }
{ object but the compiler does not inc/dec automaticlly }
{ ----------------------------------------------------------------------------- }
{ TReferenceCountedList }
procedure TReferenceCountedList.AddRef;
begin
InterlockedIncrement(FRefCount)
end;
procedure TReferenceCountedList.Release;
begin
InterlockedDecrement (FRefCount);
if FRefCount <= 0 then
Free;
end;
{ TShellSortHelper }
function TShellSortHelper.CompareIDSort(SortColumn: integer; NS1,
NS2: TNamespace): Integer;
begin
if Assigned(NS1.ParentShellFolder) then
begin
Result := NS2.ComparePIDL(NS1.RelativePIDL, False, SortColumn);
{ If we are not sorting the Name column then do a sub-sort on the name if }
{ the items are equal. }
if (SortColumn > 0) and (Result = 0) then
Result := WideCompareText(NS1.NameInFolder, NS2.NameInFolder)
end else
Result := 0;
end;
function TShellSortHelper.DiscriminateFolders(NS1,
NS2: TNamespace): Integer;
begin
Result := 0;
if NS1.Folder xor NS2.Folder then
begin
if NS1.Folder and not NS2.Folder then
Result := -1
else
if not NS1.Folder and NS2.Folder then
Result := 1
end
end;
function TShellSortHelper.SortFileSize(NS1, NS2: TNamespace): Integer;
begin
Result := DiscriminateFolders(NS1, NS2);
if Result = 0 then
begin
if NS1.SizeOfFileInt64 > NS2.SizeOfFileInt64 then
Result := 1
else
if NS1.SizeOfFileInt64 < NS2.SizeOfFileInt64 then
Result := -1
else
Result := CompareIDSort(0, NS1, NS2)
end
end;
function TShellSortHelper.SortFileTime(FT1, FT2: TFileTime; NS1,
NS2: TNamespace): Integer;
begin
Result := DiscriminateFolders(NS1, NS2);
if Result = 0 then
begin
Result := CompareFileTime(FT1, FT2);
if Result = 0 then
Result := CompareIDSort(0, NS1, NS2)
end
end;
function TShellSortHelper.SortString(S1, S2: WideString; NS1,
NS2: TNamespace): Integer;
begin
Result := DiscriminateFolders(NS1, NS2);
if Result = 0 then
begin
Result := WideCompareText(S1, S2);
if Result = 0 then
Result := CompareIDSort(0, NS1, NS2)
end
end;
function TShellSortHelper.SortType(NS1, NS2: TNamespace): Integer;
begin
if FileSort = fsFileType then
Result := SortString(NS1.FileType, NS2.FileType, NS1, NS2)
else begin
{ Must be fsFileExtension }
Result := DiscriminateFolders(NS1, NS2);
if Result = 0 then
begin
if NS1.FileSystem and NS2.FileSystem then
begin
Result := SortString(ExtractFileExt(NS1.NameParseAddress), ExtractFileExt(NS2.NameParseAddress), NS1, NS2);
if Result = 0 then
CompareIDSort(0, NS1, NS2); // Secondary sort
end;
end
end
end;
{$IFNDEF COMPILER_5_UP}
{ TObjectList }
function TObjectList.Add(AObject: TObject): Integer;
begin
Result := inherited Add(AObject);
end;
constructor TObjectList.Create;
begin
inherited Create;
FOwnsObjects := True;
end;
constructor TObjectList.Create(AOwnsObjects: Boolean);
begin
inherited Create;
FOwnsObjects := AOwnsObjects;
end;
function TObjectList.FindInstanceOf(AClass: TClass; AExact: Boolean;
AStartAt: Integer): Integer;
var
I: Integer;
begin
Result := -1;
for I := AStartAt to Count - 1 do
if (AExact and
(Items[I].ClassType = AClass)) or
(not AExact and
Items[I].InheritsFrom(AClass)) then
begin
Result := I;
break;
end;
end;
function TObjectList.First: TObject;
begin
Result := TObject(inherited First);
end;
function TObjectList.GetItem(Index: Integer): TObject;
begin
Result := inherited Items[Index];
end;
function TObjectList.IndexOf(AObject: TObject): Integer;
begin
Result := inherited IndexOf(AObject);
end;
procedure TObjectList.Insert(Index: Integer; AObject: TObject);
begin
inherited Insert(Index, AObject);
end;
function TObjectList.Last: TObject;
begin
Result := TObject(inherited Last);
end;
function TObjectList.Remove(AObject: TObject): Integer;
begin
Result := inherited Remove(AObject);
end;
procedure TObjectList.SetItem(Index: Integer; AObject: TObject);
begin
inherited Items[Index] := AObject;
end;
{$ENDIF}
{ TVirtualNamespaceList }
function TVirtualNamespaceList.Add(ANamespace: TNamespace): Integer;
begin
Result := inherited Add(ANamespace);
end;
procedure TVirtualNamespaceList.FillArray(var NamespaceArray: TNamespaceArray);
var
I: Integer;
begin
SetLength(NamespaceArray, Count);
for I := 0 to Count - 1 do
NamespaceArray[0] := Items[I];
end;
function TVirtualNamespaceList.GetItems(Index: Integer): TNamespace;
begin
Result := TNamespace(inherited Items[Index]);
end;
function TVirtualNamespaceList.IndexOf(ANamespace: TNamespace): Integer;
begin
Result := inherited IndexOf(ANamespace);
end;
procedure TVirtualNamespaceList.Insert(Index: Integer; ANamespace: TNamespace);
begin
inherited Insert(Index, ANamespace);
end;
procedure TVirtualNamespaceList.SetItems(Index: Integer; ANamespace: TNamespace);
begin
inherited Items[Index] := ANamespace;
end;
{ TCommonShellContextMenu }
constructor TCommonShellContextMenu.Create(AOwner: TComponent);
begin
inherited;
{$IFDEF TNTSUPPORT}
KeyStrings := TTntStringList.Create;
{$ELSE}
KeyStrings := TStringList.Create;
{$ENDIF}
MenuMap := TMenuItemMap.Create;
KeyStrings.Duplicates := dupIgnore;
KeyStrings.Sorted := True;
Stub := CreateStub(Self, @TCommonShellContextMenu.DefMenuCreateCallback);
FRenameMenuItem := True;
FPasteMenuItem := True;
end;
destructor TCommonShellContextMenu.Destroy;
begin
DisposeStub(Stub);
FreeAndNil(FKeyStrings);
inherited Destroy;
end;
function TCommonShellContextMenu.BindToObject(pidl: PItemIDList; pbcReserved: Pointer; const riid: TIID; out ppvOut{$IFNDEF COMPILER_5_UP}: Pointer{$ENDIF}): HResult;
begin
Result := ActiveFolder.BindToObject(pidl, pbcReserved, riid, ppvOut);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('BindToObject - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.BindToStorage(pidl: PItemIDList; pbcReserved: Pointer; const riid: TIID; out ppvObj{$IFNDEF COMPILER_5_UP}: Pointer{$ENDIF}): HResult;
begin
Result := ActiveFolder.BindToStorage(pidl, pbcReserved, riid, ppvObj);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('BindToStorage - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.CompareIDs(lParam: LPARAM; pidl1, pidl2: PItemIDList): HResult;
begin
Result := ActiveFolder.CompareIDs(lParam, pidl1, pidl2);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('CompareIDs - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.CreateViewObject(hwndOwner: HWND; const riid: TIID; out ppvOut{$IFNDEF COMPILER_5_UP}: Pointer{$ENDIF}): HResult;
begin
Result := ActiveFolder.CreateViewObject(hwndOwner, riid, ppvOut);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('CreateViewObject - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.DefMenuCreateCallback(const psf: IShellfolder; wnd: HWND; const pdtObj: IDataObject; uMsg: UINT; WParm: WParam; lParm: LParam): HResult;
var
DoDefault: Boolean;
IDO: IDataObject;
QCMInfo: PQCMINFO;
DFMICS: PDFMICS;
MapCount, i: Integer;
MergeOffset: UINT;
{$IFDEF TNTSUPPORT}
MenuItem: TTntMenuItem;
{$ELSE}
MenuItem: TMenuItem;
{$ENDIF}
begin
Result := E_NOTIMPL;
DoDefault := True;
case uMsg of
DFM_MERGECONTEXTMENU:
begin
if cmeShellDefault in Extensions then
Result := S_OK
else
Result := S_FALSE;
QCMInfo := PQCMINFO( lParm);
// This seems to be broken. The items added "normally" (using idCmdFirst as is)
// don't get registered and send a 0 when selected. DFM_MERGECONTEXTMENUTOP works
// but the return in DFM_INVOKECOMMAND is the OFFSET of the idCmdFirst passed
// so we have to make sure that the offset does not overlap the idCmdFirst we
// use here so choose a large enough number. If we increase idCmdFirst from
// its passed value it seems to make it work and in DFM_INVOKECOMMAND we get that
// the (Value - 1) rather then then Offset.
MapCount := MenuMap.Count;
MergeOffset := QCMInfo^.idCmdFirst;
QCMInfo^.idCmdFirst := QCMInfo^.idCmdFirst + 200; // Allow up to 200 items to be added in DFM_MERGECONTEXTMENUTOP;
DoMenuMerge(QCMInfo^.Menu, QCMInfo^.IndexMenu, QCMInfo^.idCmdFirst, QCMInfo^.idCmdLast, CFM_FLAGSToShellContextMenuFlags(WParm));
// Fix the MenuIDs to be commonly offset once all merging is done
if MenuMap.Count > MapCount then
for i := MapCount to MenuMap.Count - 1 do
MenuMap[i].MenuID := MenuMap[i].MenuID - MergeOffset;
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_MERGECONTEXTMENU');
{$ENDIF}
end;
DFM_INVOKECOMMAND:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND');
{$ENDIF}
case Cardinal( wParm) of
DFM_CMD_DELETE:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_DELETE');
{$ENDIF}
DoDelete(psf, pdtObj, DoDefault);
end;
DFM_CMD_CUT:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_CUT');
{$ENDIF}
DoCut(psf, pdtObj, DoDefault)
end;
DFM_CMD_COPY:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_COPY');
{$ENDIF}
DoCopy(psf, pdtObj, DoDefault)
end;
DFM_CMD_CREATESHORTCUT:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_CREATESHORTCUT');
{$ENDIF}
DoCreateShortCut(psf, pdtObj, DoDefault)
end;
DFM_CMD_PROPERTIES:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_PROPERTIES');
{$ENDIF}
DoProperties(psf, pdtObj, DoDefault);
if DoDefault and not IsWinVistaOrUp then
begin
if Assigned(SHMultiFileProperties_MP) then
begin
CreateFullyQualifiedShellDataObject(NamespaceToAbsolutePIDLArray(LocalNamespaces), IDO);
if Assigned(IDO) then
SHMultiFileProperties_MP(IDO, 0);
// Not sure why I have to do this but I have to in order for the counting to work right..
IDO._Release;
end else
DesktopFolder.ShowPropertySheetMulti(LocalNamespaces, False, True);
DoDefault := False
end
end;
DFM_CMD_NEWFOLDER:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_NEWFOLDER');
{$ENDIF}
DoNewFolder(psf, pdtObj, DoDefault)
end;
DFM_CMD_PASTE:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_PASTE');
{$ENDIF}
DoPaste(psf, pdtObj, DoDefault)
end;
DFM_CMD_VIEWLIST:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_VIEWLIST');
{$ENDIF}
end;
DFM_CMD_VIEWDETAILS:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_VIEWDETAILS');
{$ENDIF}
end;
DFM_CMD_PASTELINK:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_PASTELINK');
{$ENDIF}
DoPasteLink(psf, pdtObj, DoDefault)
end;
DFM_CMD_PASTESPECIAL:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_PasteShortCut');
{$ENDIF}
DoPasteShortCut(psf, pdtObj, DoDefault)
end;
DFM_CMD_MODALPROP:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMAND: DFM_CMD_MODALPROP');
{$ENDIF}
end;
end;
if DoDefault then
Result := S_FALSE
else
Result := S_OK
end;
DFM_CREATE:
begin
DoShow;
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_CREATE');
{$ENDIF}
end;
DFM_DESTROY:
begin
DoHide;
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_DESTROY');
{$ENDIF}
end;
DFM_GETHELPTEXTA:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_GETHELPTEXTA');
{$ENDIF}
end;
DFM_MEASUREITEM:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_MEASUREITEM');
{$ENDIF}
end;
DFM_DRAWITEM:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_DRAWITEM');
{$ENDIF}
end;
DFM_INITMENUPOPUP:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INITMENUPOPUP');
{$ENDIF}
end;
DFM_VALIDATECMD:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_VALIDATECMD');
{$ENDIF}
end;
DFM_MERGECONTEXTMENU_TOP:
begin
if cmeShellDefault in Extensions then
Result := S_OK
else
Result := S_FALSE;
QCMInfo := PQCMINFO( lParm);
// This seems to be broken. The items added "normally" (using idCmdFirst as is)
// don't get registered and send a 0 when selected. DFM_MERGECONTEXTMENUTOP works
// but the return in DFM_INVOKECOMMAND is the OFFSET of the idCmdFirst passed
// so we have to make sure that the offset does not overlap the idCmdFirst we
// use here so choose a large enough number. If we increase idCmdFirst from
// its passed value it seems to make it work and in DFM_INVOKECOMMAND we get that
// the (Value - 1) rather then then Offset.
MapCount := MenuMap.Count;
MergeOffset := QCMInfo^.idCmdFirst;
QCMInfo^.idCmdFirst := QCMInfo^.idCmdFirst + 600; // Allow up to 200 items to be added in DFM_MERGECONTEXTMENU;
DoMenuMergeTop(QCMInfo^.Menu, QCMInfo^.IndexMenu, QCMInfo^.idCmdFirst, QCMInfo^.idCmdLast, CFM_FLAGSToShellContextMenuFlags(WParm));
DoMenuMergeBottom(QCMInfo^.Menu, GetMenuItemCount(QCMInfo^.Menu), QCMInfo^.idCmdFirst, QCMInfo^.idCmdLast, CFM_FLAGSToShellContextMenuFlags(WParm));
// Fix the MenuIDs to be commonly offset once all merging is done
if MenuMap.Count > MapCount then
for i := MapCount to MenuMap.Count - 1 do
MenuMap[i].MenuID := MenuMap[i].MenuID - MergeOffset;
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_MERGECONTEXTMENU_TOP');
{$ENDIF}
end;
DFM_MERGECONTEXTMENU_BOTTOM:
begin
// Only works in XP and up. Faked in DFM_MERGECONTEXTMENU_TOP
if cmeShellDefault in Extensions then
Result := S_OK
else
Result := S_FALSE;
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_MERGECONTEXTMENU_BOTTOM');
{$ENDIF}
end;
DFM_GETHELPTEXTW:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_GETHELPTEXTW');
{$ENDIF}
end;
DFM_INVOKECOMMANDEX:
begin
DFMICS := PDFMICS( lparm);
if FindCommandId(wParm, MenuItem) then
DoInvokeCommand(MenuItem, DFMICS^.pici);
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_INVOKECOMMANDEX');
{$ENDIF}
end;
DFM_MAPCOMMANDNAME:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_MAPCOMMANDNAME');
{$ENDIF}
end;
DFM_GETDEFSTATICID:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_GETDEFSTATICID');
{$ENDIF}
end;
DFM_GETVERBW:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_GETVERBW');
{$ENDIF}
end;
DFM_GETVERBA:
begin
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('DFM_GETVERBA');
{$ENDIF}
end;
else
{$IFDEF GXDEBUG_DEFMENUCREATE_CALLBACK}
SendDebug('Undefined: ' + IntToStr(UMsg));
{$ENDIF}
end;
end;
function TCommonShellContextMenu.DragEnter(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult;
begin
Result := S_OK;
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('DragEnter - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.DragLeave: HResult;
begin
Result := S_OK;
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('DragLeave - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult;
begin
Result := E_NOTIMPL;
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('DragOver - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult;
begin
Result := E_NOTIMPL;
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('Drop - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.DuplicateKey(Key: HKEY): HKEY;
begin
if Assigned(RegOpenKeyExW_MP) then
RegOpenKeyExW_MP(Key, '', 0, MAXIMUM_ALLOWED, Result)
else
RegOpenKeyExA(Key, '', 0, MAXIMUM_ALLOWED, Result)
end;
function TCommonShellContextMenu.EnumObjects(hwndOwner: HWND; grfFlags: DWORD; out EnumIDList: IEnumIDList): HResult;
begin
Result := ActiveFolder.EnumObjects(hwndOwner, grfFlags, EnumIDList);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('EnumObjects - Result = ' + InttoStr(Result));
{$ENDIF}
end;
{$IFDEF TNTSUPPORT}
function TCommonShellContextMenu.FindCommandId(CmdID: UINT; var MenuItem: TTntMenuItem): Boolean;
var
i: Integer;
begin
Result := False;
MenuItem := nil;
i := 0;
while not Result and (i < MenuMap.Count) do
begin
Result := MenuMap[i].MenuID = CmdID;
if Result then
MenuItem := MenuMap[i].Item
else
Inc(i)
end
end;
{$ELSE}
function TCommonShellContextMenu.FindCommandId(CmdID: UINT; var MenuItem: TMenuItem): Boolean;
var
i: Integer;
begin
Result := False;
MenuItem := nil;
i := 0;
while not Result and (i < MenuMap.Count) do
begin
Result := MenuMap[i].MenuID = CmdID;
if Result then
MenuItem := MenuMap[i].Item
else
Inc(i)
end
end;
{$ENDIF}
function TCommonShellContextMenu.GetAttributesOf(cidl: UINT; var apidl: PItemIDList; var rgfInOut: UINT): HResult;
var
RequestedFlags: UINT;
begin
RequestedFlags := rgfInOut;
Result := ActiveFolder.GetAttributesOf(cidl, apidl, rgfInOut);
if RequestedFlags and SFGAO_CANCOPY <> 0 then
if CopyValidated then
rgfInOut := rgfInOut or SFGAO_CANCOPY;
if RequestedFlags and SFGAO_CANMOVE <> 0 then
if CutValidated then
rgfInOut := rgfInOut or SFGAO_CANMOVE;
end;
function TCommonShellContextMenu.GetDisplayNameOf(pidl: PItemIDList; uFlags: DWORD; var lpName: TStrRet): HResult;
begin
Result := ActiveFolder.GetDisplayNameOf(pidl, uFlags, lpName);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('GetDisplayNameOf - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.GetUIObjectOf(hwndOwner: HWND; cidl: UINT; var apidl: PItemIDList; const riid: TIID; prgfInOut: Pointer; out ppvOut{$IFNDEF COMPILER_5_UP}: Pointer{$ENDIF}): HResult;
var
DataObject: IDataObject;
begin
Result := E_NOINTERFACE;
if FromDesktop then
begin
// Need to create a correctly defined fully qualified HDROP in the DataObject
if IsEqualGUID(riid, IDataObject) then
begin
DataObject := nil;
CreateFullyQualifiedShellDataObject(ActivePIDLs, DataObject);
IDataObject( ppvOut) := DataObject;
UIObjectOfDataObject := IDataObject( ppvOut);
if Assigned(IDataObject( ppvOut)) then
Result := S_OK
end else
if IsEqualGUID(riid, IDropTarget) then
begin
if PasteMenuItem then
begin
// Need to support this to get a Paste menu item
IDropTarget( ppvOut) := Self;
UIObjectOfDropTarget := IDropTarget( ppvOut);
Result := S_OK
end
end else
Result := ActiveFolder.GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut)
end else
Result := ActiveFolder.GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('GetUIObjectOf - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.ParseDisplayName(hwndOwner: HWND; pbcReserved: Pointer; lpszDisplayName: POLESTR; out pchEaten: ULONG; out ppidl: PItemIDList; var dwAttributes: ULONG): HResult;
begin
Result := ActiveFolder.ParseDisplayName(hwndOwner, pbcReserved, lpszDisplayName, pchEaten, ppidl, dwAttributes);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('ParseDisplayName - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.SetNameOf(hwndOwner: HWND; pidl: PItemIDList; lpszName: POLEStr; uFlags: DWORD; var ppidlOut: PItemIDList): HResult;
begin
Result := ActiveFolder.SetNameOf(hwndOwner, pidl, lpszName, uFlags, ppidlOut);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('SetNameOf - Result = ' + InttoStr(Result));
{$ENDIF}
end;
function TCommonShellContextMenu.InternalShowContextMenu(Owner: TWinControl; ParentPIDL: PItemIDList; ChildPIDLs: TAbsolutePIDLArray; Verb: WideString; Position: PPoint = nil): Boolean;
//
// If ParentPIDL is nil the assumption is that the Parent is the
// Desktop and the PIDLs are fully qualified PIDLs. If ParentPIDL is assigned then
// the PIDLs MUST be immediate children of the ParentPIDL
//
const
MaxVerbLen = 128;
type
THKeyArray = array of HKey;
procedure AddKey(Key: HKey; var KeyArray: THKeyArray);
begin
SetLength(KeyArray, Length(KeyArray) + 1);
KeyArray[Length(KeyArray)-1] := Key
end;
var
Menu: hMenu;
InvokeInfo: TCMInvokeCommandInfoEx;
MenuCmd: Cardinal;
x, y, i: integer;
VerbA: string;
VerbW: WideString;
GenericVerb: Pointer;
Handled, AllowShow, Success: Boolean;
Flags: Longword;
ContextMenu: IContextMenu;
ContextMenu2: IContextMenu2;
ContextMenu3: IContextMenu3;
OldMode: UINT;
Keys: THKeyArray;
Desktop: IShellFolder;
DesktopPIDL, ChildrenPIDLs: PItemIDList;
{$IFDEF TNTSUPPORT}
Reg: TTntRegistry;
{$ELSE}
Reg: TRegistry;
{$ENDIF}
WS, CurVer: WideString;
UnknownAdded: Boolean;
begin
Result := False;
DesktopPIDL := nil;
Desktop := nil;
begin
OldMode := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
try
try
Result := False;
Assert(Assigned(Owner), 'To show a Context Menu using TNamespace you must pass a valid Owner TWinControl');
if Assigned(Owner) then
begin
if Assigned(CDefFolderMenu_Create2_MP) then
begin
FOldWndProcForContextMenu := Owner.WindowProc;
try
// Hook the owner for the Window message for owner draw menus like
// Send To..
Owner.WindowProc := WindowProcForContextMenu;
ContextMenu := nil;
ContextMenu2 := nil;
ContextMenu3 := nil;
Result := False;
if Assigned(Position) then
begin
x := Position.x;
y := Position.y
end else
begin
x := Mouse.CursorPos.X; // Snag these fast. The mouse can move a fair amount
y := Mouse.CursorPos.Y; // before the popup menu is shown.
end;
FillChar(InvokeInfo, SizeOf(InvokeInfo), #0);
Menu := CreatePopupMenu;
try
Flags := CMF_NORMAL or CMF_EXPLORE;
if RenameMenuItem then
Flags := Flags or CMF_CANRENAME;
if GetKeyState(VK_SHIFT) and $8000 <> 0 then
Flags := Flags or CMF_EXTENDEDVERBS;
if cmeDirectory in Extensions then
AddMenuKey('Directory');
if cmeDirBackground in Extensions then
AddMenuKey('Directory\Background');
if cmeFolder in Extensions then
AddMenuKey('Folder');
if cmeAsterik in Extensions then
AddMenuKey('*');
if cmeAllFilesystemObjects in Extensions then
AddMenuKey('AllFilesystemObjects');
// Need to rework this to do it right
// 1) look at the extension under HKCR, read default value
// 2) open key pointed to by above default value (file class)
// 3) see if file class has a "CurVer" key
// 4) if so open that key as the current version of the file class
// 5) open HKCR/SystemFileAssociations for the file extension
// 6) if 1 was unsuccessful (no association) use the "Unknown" key
// 7) see if the extension or file class key have \PerceivedType key
// 8) if so look in HKCR/SystemFileAssociations for a key of the perceived type from above
//
// Aug 25th - If I just pass the ext OR the file class then the Merge Extended items works ("Open" for instance)
UnknownAdded := False;
{$IFDEF TNTSUPPORT}
Reg := TTntRegistry.Create;
{$ELSE}
Reg := TRegistry.Create;
{$ENDIF}
try
Reg.RootKey := HKEY_CLASSES_ROOT;
for i := 0 to KeyStrings.Count - 1 do
begin
if (KeyStrings[i] <> '') then
begin
if Reg.OpenKeyReadOnly(KeyStrings[i]) then
begin
// The extension key is open {.ext}
AddKey(DuplicateKey(Reg.CurrentKey), Keys);
WS := Reg.ReadString('');
Reg.CloseKey;
if WS <> '' then
begin
if Reg.OpenKeyReadOnly(WS) then
begin
// The FileClass is open, see if there is mulitiple version and a current version
if Reg.KeyExists('CurVer') then
begin
if Reg.OpenKeyReadOnly('CurVer') then
begin
CurVer := Reg.ReadString('');
if Reg.OpenKeyReadOnly('\' + CurVer) then
AddKey(DuplicateKey(Reg.CurrentKey), Keys);
end;
Reg.CloseKey;
end else
begin
AddKey(DuplicateKey(Reg.CurrentKey), Keys);
Reg.CloseKey;
end
end
end
end else
begin
if not UnknownAdded and Reg.OpenKeyReadOnly('Unknown') then
begin
AddKey(DuplicateKey(Reg.CurrentKey), Keys);
Reg.CloseKey;
UnknownAdded := True
end
end;
if cmeFileSystemAssociations in Extensions then
begin
if Reg.OpenKeyReadOnly('SystemFileAssociations\' + KeyStrings[i]) then
begin
AddKey(DuplicateKey(Reg.CurrentKey), Keys);
Reg.CloseKey;
end
end;
if cmePerceivedType in Extensions then
begin
if Reg.OpenKeyReadOnly(KeyStrings[i]) then
begin
WS := Reg.ReadString('PerceivedType');
if WS <> '' then
begin
if Reg.OpenKeyReadOnly('\SystemFileAssociations\' + WS) then
AddKey(DuplicateKey(Reg.CurrentKey), Keys);
end;
Reg.CloseKey;
end
end
end
end
finally
Reg.CloseKey;
Reg.Free
end;
Success := False;
ActivePIDLs := ChildPIDLs;
if not Assigned(ParentPIDL) and Assigned(ChildPIDLs) then
begin
FromDesktop := True;
SHGetDesktopFolder(FActiveFolder);
SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, DesktopPIDL);
Success := CDefFolderMenu_Create2_MP(DesktopPIDL, Owner.Handle,
Length(ChildPIDLs), PItemIDList(ChildPIDLs[0]), Self,
Stub, Length(Keys), PHKey(@Keys[0]), ContextMenu) = S_OK;
end else
begin
FromDesktop := False;
SHGetDesktopFolder(Desktop);
if PIDLMgr.IsDesktopFolder(ParentPIDL) then
FActiveFolder := Desktop
else
Desktop.BindToObject(ParentPIDL, nil, IShellFolder, Pointer( FActiveFolder));
if Assigned(ActiveFolder) then
begin
if Assigned(ParentPIDL) and Assigned(ChildPIDLs) then
Success := CDefFolderMenu_Create2_MP(ParentPIDL, Owner.Handle,
Length(ChildPIDLs), PItemIDList(ChildPIDLs[0]),
Self, Stub, Length(Keys), PHKey(@Keys[0]), ContextMenu) = S_OK
else begin
// Must be a background menu call
ChildrenPIDLs := nil;
if Assigned(ParentPIDL) and not Assigned(ChildPIDLs) then
Success := CDefFolderMenu_Create2_MP(ParentPIDL, Owner.Handle,
0, ChildrenPIDLs, Self, Stub, Length(Keys),
PHKey(@Keys[0]), ContextMenu) = S_OK
end
end
end;
if Success then
begin
CurrentContextMenu := ContextMenu;
CurrentContextMenu2 := nil; // not sure it is available yet
if Assigned(ContextMenu) then
begin
if ContextMenu.QueryInterface(IContextMenu3, Pointer(ContextMenu3)) = E_NOINTERFACE then
begin
if ContextMenu.QueryInterface(IID_IContextMenu2, Pointer(ContextMenu2)) <> E_NOINTERFACE then
CurrentContextMenu2 := ContextMenu2;
end else
CurrentContextMenu2 := ContextMenu3;
if Assigned(ContextMenu3) then
ContextMenu3.QueryContextMenu(Menu, 0, 1, $7FFF, Flags)
else
if Assigned(ContextMenu2) then
ContextMenu2.QueryContextMenu(Menu, 0, 1, $7FFF, Flags)
else
if Assigned(ContextMenu) then
ContextMenu.QueryContextMenu(Menu, 0, 1, $7FFF, Flags);
// May just be executing a verb
AllowShow := Verb = '';
if AllowShow then
MenuCmd := Cardinal( TrackPopupMenuEx(
Menu,
TPM_LEFTALIGN or TPM_RETURNCMD or TPM_RIGHTBUTTON,
x, y, Owner.Handle, nil))
else
MenuCmd := 0;
if (MenuCmd <> 0) or (Verb <> '') then
begin
if (MenuCmd <> 0) then
begin
if IsUnicode then
begin
SetLength(VerbW, MaxVerbLen);
GenericVerb := @VerbW[1];
Flags := GCS_VERBW
end else
begin
SetLength(VerbA, MaxVerbLen);
GenericVerb := @VerbA[1];
Flags := GCS_VERBA
end;
if Assigned(ContextMenu3) then
Result := Succeeded(ContextMenu3.GetCommandString(MenuCmd-1, Flags, nil, GenericVerb, MaxVerbLen))
else
if Assigned(ContextMenu2) then
Result := Succeeded(ContextMenu2.GetCommandString(MenuCmd-1, Flags, nil, GenericVerb, MaxVerbLen))
else
if Assigned(ContextMenu) then
Result := Succeeded(ContextMenu.GetCommandString(MenuCmd-1, Flags, nil, GenericVerb, MaxVerbLen));
if IsUnicode then
SetLength(VerbW, lstrlenW(PWideChar( VerbW)))
else begin
SetLength(VerbA, StrLen(PChar( VerbA)));
VerbW := VerbA
end;
end else
begin
VerbW := Verb;
VerbA := Verb;
Result := True;
end;
if not Result then
VerbW := STR_UNKNOWNCOMMAN;
Handled := False;
if not Handled then
begin
FillChar(InvokeInfo, SizeOf(InvokeInfo), #0);
with InvokeInfo do
begin
if MenuCmd > 0 then
begin
{ For some reason the lpVerbW won't work }
lpVerb := MakeIntResourceA(MenuCmd-1);
if IsUnicode then
begin
fMask := CMIC_MASK_UNICODE;
lpVerbW := MakeIntResourceW(MenuCmd-1);
end;
end else
begin
if IsUnicode then
begin
fMask := CMIC_MASK_UNICODE;
lpVerbW := PWideChar( VerbW);
lpVerb := PChar( VerbA)
end else
lpVerb := PChar( VerbA)
end;
// Win95 get confused if size = TCMInvokeCommandInfoEx
if IsUnicode then
cbSize := SizeOf(TCMInvokeCommandInfoEx)
else
cbSize := SizeOf(TCMInvokeCommandInfo);
hWnd := Owner.Handle;
nShow := SW_SHOWNORMAL;
end;
if Assigned(ContextMenu3) then
Result := Succeeded(ContextMenu3.InvokeCommand(InvokeInfo))
else
if Assigned(ContextMenu2) then
Result := Succeeded(ContextMenu2.InvokeCommand(InvokeInfo))
else
if Assigned(ContextMenu) then
Result := Succeeded(ContextMenu.InvokeCommand(InvokeInfo));
end
end
end
end
finally
PIDLMgr.FreePIDL(DesktopPIDL);
CurrentContextMenu := nil;
CurrentContextMenu2 := nil;
ContextMenu := nil;
ContextMenu2 := nil;
ContextMenu3 := nil;
FActiveFolder := nil;
Desktop := nil;
// Special handling because this is a non reference counted Component
if Assigned(UIObjectOfDataObject) then
UIObjectOfDataObject._Release;
UIObjectOfDataObject := nil;
if Assigned(UIObjectOfDropTarget) then
UIObjectOfDropTarget._Release;
UIObjectOfDropTarget := nil;
{ Don't access any properties or field of the object. If the verb is }
{ 'delete' the component using this class could have freed the instance }
{ of the object through a ShellNotifyRegister or some other way. }
DestroyMenu(Menu);
for i := 0 to Length(Keys) - 1 do
RegCloseKey(Keys[i]);
Keys := nil;
end;
finally
Owner.WindowProc := FOldWndProcForContextMenu;
FOldWndProcForContextMenu := nil;
end;
end
end;
finally
SetErrorMode(OldMode);
end
except
end
end
end;
procedure TCommonShellContextMenu.AddMenuKey(Key: WideString);
begin
KeyStrings.Add(Key)
end;
{$IFDEF TNTSUPPORT}
procedure TCommonShellContextMenu.AddMenuKeys(Keys: TTntStringList);
begin
KeyStrings.Assign(Keys)
end;
{$ELSE}
procedure TCommonShellContextMenu.AddMenuKeys(Keys: TStringList);
begin
KeyStrings.Assign(Keys)
end;
{$ENDIF}
procedure TCommonShellContextMenu.ClearKeys;
begin
KeyStrings.Clear
end;
procedure TCommonShellContextMenu.ClearMenuMap;
begin
MenuMap.Clear
end;
procedure TCommonShellContextMenu.DoCopy(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean);
begin
if Assigned(OnShellMenuCopy) then
OnShellMenuCopy(Self, ShellFolder, DataObject, DoDefault)
end;
procedure TCommonShellContextMenu.DoCreateShortCut(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean);
begin
if Assigned(OnShellMenuCreateShortCut) then
OnShellMenuCreateShortCut(Self, ShellFolder, DataObject, DoDefault)
end;
procedure TCommonShellContextMenu.DoCut(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean);
begin
if Assigned(OnShellMenuCut) then
OnShellMenuCut(Self, ShellFolder, DataObject, DoDefault)
end;
procedure TCommonShellContextMenu.DoDelete(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean);
begin
if Assigned(OnShellMenuDelete) then
OnShellMenuDelete(Self, ShellFolder, DataObject, DoDefault)
end;
procedure TCommonShellContextMenu.DoHide;
begin
if Assigned(OnHide) then
OnHide(Self);
end;
{$IFDEF TNTSUPPORT}
procedure TCommonShellContextMenu.DoInvokeCommand(MenuItem: TTntMenuItem; InvokeInfo: PCMInvokeCommandInfo);
var
Handled: Boolean;
begin
Handled := False;
if Assigned(OnInvokeCommand) then
OnInvokeCommand(Self, MenuItem, InvokeInfo, Handled);
if not Handled then
MenuItem.Click;
end;
{$ELSE}
procedure TCommonShellContextMenu.DoInvokeCommand(MenuItem: TMenuItem; InvokeInfo: PCMInvokeCommandInfo);
var
Handled: Boolean;
begin
Handled := False;
if Assigned(OnInvokeCommand) then
OnInvokeCommand(Self, MenuItem, InvokeInfo, Handled);
if not Handled then
MenuItem.Click;
end;
{$ENDIF}
procedure TCommonShellContextMenu.DoMenuMerge(Menu: HMENU; IndexMenu: UINT;
var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags);
begin
if Assigned(OnMenuMerge) then
OnMenuMerge(Self, Menu, IndexMenu, CmdFirst, CmdLast, Flags);
end;
procedure TCommonShellContextMenu.DoMenuMergeBottom(Menu: HMENU;
IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT;
Flags: TShellContextMenuFlags);
begin
if Assigned(OnMenuMergeBottom) then
OnMenuMergeBottom(Self, Menu, IndexMenu, CmdFirst, CmdLast, Flags);
end;
procedure TCommonShellContextMenu.DoMenuMergeTop(Menu: HMENU; IndexMenu: UINT;
var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags);
begin
if Assigned(OnMenuMergeTop) then
OnMenuMergeTop(Self, Menu, IndexMenu, CmdFirst, CmdLast, Flags);
end;
procedure TCommonShellContextMenu.DoNewFolder(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean);
begin
if Assigned(OnShellMenuNewFolder) then
OnShellMenuNewFolder(Self, ShellFolder, DataObject, DoDefault)
end;
procedure TCommonShellContextMenu.DoPaste(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean);
begin
if Assigned(OnShellMenuPaste) then
OnShellMenuPaste(Self, ShellFolder, DataObject, DoDefault)
end;
procedure TCommonShellContextMenu.DoPasteLink(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean);
begin
if Assigned(OnShellMenuPasteLink) then
OnShellMenuPasteLink(Self, ShellFolder, DataObject, DoDefault)
end;
procedure TCommonShellContextMenu.DoPasteShortCut(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean);
begin
if Assigned(OnShellMenuPasteShortCut) then
OnShellMenuPasteShortCut(Self, ShellFolder, DataObject, DoDefault)
end;
procedure TCommonShellContextMenu.DoProperties(ShellFolder: IShellFolder; DataObject: IDataObject; var DoDefault: Boolean);
begin
if Assigned(OnShellMenuProperites) then
OnShellMenuProperites(Self, ShellFolder, DataObject, DoDefault)
end;
procedure TCommonShellContextMenu.DoShow;
begin
if Assigned(OnShow) then
OnShow(Self);
end;
procedure TCommonShellContextMenu.HandleContextMenuMsg(Msg, wParam, lParam: Longint; var Result: LRESULT);
{ This is called when the ContextMenu calls back to its owner window to ask }
{ questions to implement the addition of icons to the menu. The messages sent }
{ to the owner window are: WM_INITMENUPOPUP, WM_DRAWITEM, or WM_MEASUREITEM. }
{ Which must be passed on to the ContextMenu2 interface to display items with }
{ icons. }
var
ContextMenu3: IContextMenu3;
begin
if Assigned(CurrentContextMenu2) then
if CurrentContextMenu2.QueryInterface(IContextMenu3, ContextMenu3) <> E_NOINTERFACE then
Result := ContextMenu3.HandleMenuMsg2(Msg, wParam, lParam, Result)
else
Result := CurrentContextMenu2.HandleMenuMsg(Msg, wParam, lParam);
end;
procedure TCommonShellContextMenu.LoadMultiFolderPIDLArray(Namespaces: TNamespaceArray; var PIDLs: TAbsolutePIDLArray);
var
i: Integer;
begin
CopyValidated := True;
CutValidated := True;
SetLength(PIDLs, Length(Namespaces));
for i := 0 to Length(Namespaces) - 1 do
begin
PIDLs[i] := Namespaces[i].AbsolutePIDL;
if Namespaces[i].Folder and not Namespaces[i].Browsable then
Include(FExtensions, cmeDirectory);
if CopyValidated then
CopyValidated := Namespaces[i].CanCopy;
if CutValidated then
CutValidated := Namespaces[i].CanMove;
if Namespaces[i].Folder and not Namespaces[i].Directory then
Include(FExtensions, cmeFolder);
end;
end;
procedure TCommonShellContextMenu.WindowProcForContextMenu(var Message: TMessage);
begin
FOldWndProcForContextMenu(Message); // Call the OldWindProc of the ContextMenu owner
case Message.Msg of
WM_DRAWITEM, WM_INITMENUPOPUP, WM_MEASUREITEM, WM_MENUCHAR:
HandleContextMenuMsg(Message.Msg, Message.WParam, Message.LParam, Message.Result);
end;
end;
function TCommonShellContextMenu._AddRef: Integer;
begin
Result := -1
end;
function TCommonShellContextMenu._Release: Integer;
begin
Result := -1
end;
function TCommonShellContextMenu.QueryInterface(const IID: TGUID;
out Obj): HResult;
begin
Result := inherited QueryInterface(IID, Obj);
{$IFDEF GXDEBUG_VIRTUALCONTEXTMENU}
SendDebug('QueryInterface - ' + GUIDToInterfaceStr(IID));
{$ENDIF}
end;
{$IFDEF TNTSUPPORT}
function TCommonShellContextMenu.MergeMenuIntoContextMenu(Menu: TTntPopupMenu;
ContextMenu: HMenu; Index: Integer; idStart: UINT): Integer;
//
// Returns the ItemID of the last item it added to the ContextMenu
//
function RunMenu(MenuItem: TTntMenuItem; hPopupMenu: hMenu; MenuID: UINT): Integer;
var
i: Integer;
SubMenu: hMenu;
Map: PMenuItemLink;
NewIndex: Integer;
begin
Result := MenuID;
if MenuItem.Count > 0 then
begin
// Item has sub-items and won't take a MenuID
SubMenu := CreatePopupMenu;
NewIndex := AddContextMenuItem(hPopupMenu, MenuItem.Caption, Index, Result, SubMenu);
for i := MenuItem.Count - 1 downto 0 do
Result := RunMenu(MenuItem.Items[i] as TTntMenuItem, SubMenu, Result);
end else
NewIndex := AddContextMenuItem(hPopupMenu, MenuItem.Caption, Index, Result, 0);
if NewIndex <> $FFFF then
begin
Map := MenuMap.Add;
Map.MenuID := Result;
Map.Item := MenuItem;
Inc(Result)
end;
end;
var
i: Integer;
begin
Result := idStart;
for i := Menu.Items.Count - 1 downto 0 do
Result := RunMenu(Menu.Items[i] as TTntMenuItem, ContextMenu, Result)
end;
{$ELSE}
function TCommonShellContextMenu.MergeMenuIntoContextMenu(Menu: TPopupMenu;
ContextMenu: HMenu; Index: Integer; idStart: UINT): Integer;
//
// Returns the ItemID of the last item it added to the ContextMenu
//
function RunMenu(MenuItem: TMenuItem; hPopupMenu: hMenu; MenuID: UINT): Integer;
var
i: Integer;
SubMenu: hMenu;
Map: PMenuItemLink;
NewIndex: Integer;
begin
Result := MenuID;
if MenuItem.Count > 0 then
begin
// Item has sub-items and won't take a MenuID
SubMenu := CreatePopupMenu;
NewIndex := AddContextMenuItem(hPopupMenu, MenuItem.Caption, Index, Result, SubMenu, MenuItem.Enabled, MenuItem.Checked, MenuItem.Default);
for i := MenuItem.Count - 1 downto 0 do
Result := RunMenu(MenuItem.Items[i], SubMenu, Result);
end else
NewIndex := AddContextMenuItem(hPopupMenu, MenuItem.Caption, Index, Result, 0, MenuItem.Enabled, MenuItem.Checked, MenuItem.Default);
if NewIndex <> $FFFF then
begin
Map := MenuMap.Add;
Map.MenuID := Result;
Map.Item := MenuItem;
Inc(Result)
end;
end;
var
i: Integer;
begin
Result := idStart;
for i := Menu.Items.Count - 1 downto 0 do
Result := RunMenu(Menu.Items[i], ContextMenu, Result)
end;
{$ENDIF}
{ TCommonShellMultiParentContextMenu}
function TCommonShellMultiParentContextMenu.ExecuteContextMenuVerb(
Owner: TWinControl; Namespaces: TNamespaceArray; Verb: string): Boolean;
var
PIDLs: TAbsolutePIDLArray;
begin
PIDLs := nil;
LocalFocused := nil;
LocalNamespaces := Namespaces;
RenameMenuItem := True;
LoadRegistryKeyStrings(nil);
LoadMultiFolderPIDLArray(Namespaces, PIDLs);
Result := InternalShowContextMenu(Owner, nil, PIDLs, Verb, nil);
end;
function TCommonShellMultiParentContextMenu.ShowContextMenu(Owner: TWinControl; Focused: TNamespace; Namespaces: TNamespaceArray; Position: PPoint = nil): Boolean;
var
PIDLs: TAbsolutePIDLArray;
begin
PIDLs := nil;
LocalFocused := Focused;
LocalNamespaces := Namespaces;
RenameMenuItem := True;
LoadRegistryKeyStrings(Focused);
LoadMultiFolderPIDLArray(Namespaces, PIDLs);
Result := InternalShowContextMenu(Owner, nil, PIDLs, '', Position);
end;
procedure TCommonShellMultiParentContextMenu.LoadRegistryKeyStrings(Focused: TNamespace);
begin
KeyStrings.Clear;
Extensions := [cmeShellDefault, cmeAsterik, cmeAllFilesystemObjects, cmeFileSystemAssociations];
// Only add the menu handlers for the focused item class
if Assigned(Focused) then
begin
if Focused.Folder then
Extensions := [cmeShellDefault, {cmeDirectory,} cmeFolder]
else
AddMenuKey(WideExtractFileExt(Focused.NameForParsing));
end
end;
{ TCommonShellBackgroundContextMenu }
constructor TCommonShellBackgroundContextMenu.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FInitialItemList := TCommonPIDLList.Create;
FFinalItemList := TCommonPIDLList.Create;
FRenameMenuItem := False;
FPasteMenuItem := False;
FShowPasteItem := True;
FShowPasteShortCutItem := True;
FShowPropertiesItem := True;
FAutoDetectNewItem := True;
{$IFDEF TNTSUPPORT}
PopupMenuProperties := TTntPopupMenu.Create(Self);
PopupMenuPasteShortCut := TTntPopupMenu.Create(Self);
PopupMenuPaste := TTntPopupMenu.Create(Self);
Paste := TTntMenuItem.Create(PopupMenuPaste);
PasteShortCut := TTntMenuItem.Create(PopupMenuPasteShortCut);
Properties := TTntMenuItem.Create(PopupMenuProperties);
{$ELSE}
PopupMenuProperties := TPopupMenu.Create(Self);
PopupMenuPasteShortCut := TPopupMenu.Create(Self);
PopupMenuPaste := TPopupMenu.Create(Self);
Paste := TMenuItem.Create(PopupMenuPaste);
PasteShortCut := TMenuItem.Create(PopupMenuPasteShortCut);
Properties := TMenuItem.Create(PopupMenuProperties);
{$ENDIF}
Paste.Caption := STR_PASTELINK;
PasteShortCut.Caption := STR_PASTE;
Properties.Caption := STR_PROPERTIES;
Paste.OnClick := ClickPaste;
PasteShortCut.OnClick := ClickPasteShortCut;
Properties.OnClick := ClickProperties;
PopupMenuProperties.Items.Add(Properties);
PopupMenuPasteShortCut.Items.Add(PasteShortCut);
PopupMenuPaste.Items.Add(Paste);
end;
destructor TCommonShellBackgroundContextMenu.Destroy;
begin
FreeAndNil(FInitialItemList);
FreeAndNil(FFinalItemList);
inherited Destroy;
end;
function TCommonShellBackgroundContextMenu.EnumCallback(MessageWnd: HWnd;
APIDL: PItemIDList; AParent: TNamespace; Data: Pointer; var Terminate: Boolean): Boolean;
begin
// copy the relative PIDLs
TCommonPIDLList( Data).Add(APIDL);
Result := True;
end;
function TCommonShellBackgroundContextMenu.ShowContextMenu(
Owner: TWinControl; Root: TNamespace; Position: PPoint): Boolean;
begin
Result := False;
LocalFocused := Root;
LocalNamespaces := nil;
if Assigned(Root) then
begin
if Root.Folder then
begin
RenameMenuItem := True;
LoadRegistryKeyStrings(Root);
Result := InternalShowContextMenu(Owner, Root.AbsolutePIDL, nil, '', Position);
end
end
end;
procedure TCommonShellBackgroundContextMenu.ClickPaste(Sender: TObject);
var
NSA: TNamespaceArray;
begin
if Assigned(LocalFocused) then
begin
SetLength(NSA, 1);
NSA[0] := LocalFocused;
LocalFocused.Paste(NSA, False)
end
end;
procedure TCommonShellBackgroundContextMenu.ClickPasteShortCut(Sender: TObject);
var
NSA: TNamespaceArray;
begin
if Assigned(LocalFocused) then
begin
SetLength(NSA, 1);
NSA[0] := LocalFocused;
LocalFocused.Paste(NSA, True)
end
end;
procedure TCommonShellBackgroundContextMenu.ClickProperties(Sender: TObject);
begin
if Assigned(LocalFocused) then
LocalFocused.ShowPropertySheet
end;
procedure TCommonShellBackgroundContextMenu.DoHide;
var
i: Integer;
Found: Boolean;
NS: TNamespace;
begin
inherited DoHide;
if AutoDetectNewItem and Assigned(LocalFocused) then
begin
LocalFocused.EnumerateFolder(0, True, True, False, EnumCallback, FinalItemList);
if (InitialItemList.Count + 1) = FinalItemList.Count then
begin
// One item was added, time to find it.
PIDLListQuickSort(InitialItemList, LocalFocused.ShellFolder, 0, InitialItemList.Count - 1);
PIDLListQuickSort(FinalItemList, LocalFocused.ShellFolder, 0, FinalItemList.Count - 1);
Found := False;
i := 0;
while not Found and (i < FinalItemList.Count) do
begin
Found := LocalFocused.ShellFolder.CompareIDs(0, InitialItemList[i], FinalItemList[i]) <> 0;
if Found then
begin
NS := TNamespace.Create(FinalItemList[i], LocalFocused);
NS.FreePIDLOnDestroy := False;
DoNewItem(NS);
NS.Free
end;
Inc(i)
end
end
end;
FinalItemList.Clear;
InitialItemList.Clear;
end;
procedure TCommonShellBackgroundContextMenu.DoMenuMerge(Menu: HMENU;
IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT;
Flags: TShellContextMenuFlags);
begin
inherited DoMenuMerge(Menu, IndexMenu, CmdFirst, CmdLast, Flags);
end;
procedure TCommonShellBackgroundContextMenu.DoMenuMergeBottom(Menu: HMENU;
IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT;
Flags: TShellContextMenuFlags);
begin
if ShowPropertiesItem then
begin
if Assigned(LocalFocused) then
Properties.Enabled := LocalFocused.HasPropSheet
else
Properties.Enabled := False;
CmdFirst := MergeMenuIntoContextMenu(PopupMenuProperties, Menu, IndexMenu, CmdFirst);
AddContextMenuItem(Menu, '-', IndexMenu);
end;
inherited DoMenuMergeBottom(Menu, IndexMenu, CmdFirst, CmdLast, Flags);
end;
procedure TCommonShellBackgroundContextMenu.DoMenuMergeTop(Menu: HMENU; IndexMenu: UINT; var CmdFirst: UINT; CmdLast: UINT; Flags: TShellContextMenuFlags);
begin
Paste.Enabled := ClipboardContainsShellFormats;
PasteShortCut.Enabled := ClipboardContainsShellFormats;
if ShowPasteItem or ShowPasteShortCutItem then
AddContextMenuItem(Menu, '-', IndexMenu);
if ShowPasteItem then
CmdFirst := MergeMenuIntoContextMenu(PopupMenuPaste, Menu, IndexMenu, CmdFirst);
if ShowPasteShortCutItem then
CmdFirst := MergeMenuIntoContextMenu(PopupMenuPasteShortCut, Menu, IndexMenu, CmdFirst);
if ShowPasteItem or ShowPasteShortCutItem then
AddContextMenuItem(Menu, '-', IndexMenu);
inherited DoMenuMergeTop(Menu, IndexMenu, CmdFirst, CmdLast, Flags);
end;
procedure TCommonShellBackgroundContextMenu.DoNewItem(NS: TNamespace);
begin
if Assigned(OnNewItem) then
OnNewItem(Self, NS)
end;
procedure TCommonShellBackgroundContextMenu.DoShow;
begin
inherited DoShow;
if AutoDetectNewItem and Assigned(LocalFocused) then
begin
InitialItemList.Clear;
LocalFocused.EnumerateFolder(0, True, True, False, EnumCallback, InitialItemList)
end
end;
procedure TCommonShellBackgroundContextMenu.LoadRegistryKeyStrings(Focused: TNamespace);
begin
CopyValidated := False;
CutValidated := False;
KeyStrings.Clear;
PasteMenuItem := True;
Extensions := [cmeShellDefault, cmeDirBackground];
end;
{ TMenuItemMap }
function TMenuItemMap.Add: PMenuItemLink;
begin
New(Result);
if Assigned(Result) then
inherited Insert(Count, Result)
end;
function TMenuItemMap.First: PMenuItemLink;
begin
Result := PMenuItemLink( inherited First)
end;
function TMenuItemMap.Get(Index: Integer): PMenuItemLink;
begin
Result := PMenuItemLink( inherited Get(Index))
end;
function TMenuItemMap.IndexOf(Item: PMenuItemLink): Integer;
begin
Result := inherited IndexOf(Item)
end;
function TMenuItemMap.Last: PMenuItemLink;
begin
Result := PMenuItemLink( inherited Last)
end;
function TMenuItemMap.Remove(Item: PMenuItemLink): Integer;
begin
Result := inherited Remove(Item)
end;
procedure TMenuItemMap.Clear;
var
i: Integer;
begin
try
for i := 0 to Count - 1 do
Dispose( Items[i]);
finally
SetCount(0);
SetCapacity(0);
end;
end;
function TMenuItemMap.Insert(Index: Integer): PMenuItemLink;
begin
New(Result);
if Assigned(Result) then
inherited Insert(Index, Result)
end;
procedure TMenuItemMap.Put(Index: Integer; Item: PMenuItemLink);
begin
inherited Put(Index, Item)
end;
initialization
CoInitialize(nil);
// if IsWinNT4 then
FileIconInit(True); // This MUST be before the Namespaces are created or it won't work because the IconCache may have an icon in from the namespace
if not LoadShell32Functions then
Halt(0);
PIDLMgr := TCommonPIDLManager.Create;
DesktopFolder := CreateSpecialNamespace(CSIDL_DESKTOP);
RecycleBinFolder := CreateSpecialNamespace(CSIDL_BITBUCKET);
PhysicalDesktopFolder := CreateSpecialNamespace(CSIDL_DESKTOPDIRECTORY);
DrivesFolder := CreateSpecialNamespace(CSIDL_DRIVES);
PrinterFolder := CreateSpecialNamespace(CSIDL_PRINTERS);
HistoryFolder := CreateSpecialNamespace(CSIDL_HISTORY);
ControlPanelFolder := CreateSpecialNamespace(CSIDL_CONTROLS);
NetworkNeighborHoodFolder := CreateSpecialNamespace(CSIDL_NETWORK);
TemplatesFolder := CreateSpecialNamespace(CSIDL_TEMPLATES);
MyDocumentsFolder := CreateSpecialNamespace(CSIDL_PERSONAL);
FavoritesFolder := CreateSpecialNamespace(CSIDL_FAVORITES);
UserDocumentsFolder := CreateSpecialNamespace(CSIDL_APPDATA);
ProgramFilesFolder := CreateSpecialNamespace(CSIDL_PROGRAMS);
finalization
FreeAndNil(DesktopFolder);
FreeAndNil(RecycleBinFolder);
FreeAndNil(PhysicalDesktopFolder);
FreeAndNil(DrivesFolder);
FreeAndNil(HistoryFolder);
FreeAndNil(PrinterFolder);
FreeAndNil(ControlPanelFolder);
FreeAndNil(NetworkNeighborHoodFolder);
FreeAndNil(TemplatesFolder);
FreeAndNil(MyDocumentsFolder);
FreeAndNil(FavoritesFolder);
FreeAndNil(UserDocumentsFolder);
FreeAndNil(ProgramFilesFolder);
FreeAndNil(PIDLMgr);
CoUninitialize;
end.