ShellPlus Customer login
ShellPlus
Shell+ components
    Home
    News
    Overview
    Download
    Examples
    Customer's area
    Flash Demo
 
Developer Tools
    Shell Reset Tool
 
Shell+ Tutorial
    Namespace Extensions
    ShortCut menu
    Property Sheet
    Icon Handler
    Thumbnails
    InfoTip Handler
    Copy Hook
    Balloon TrayIcon
    Control Panel
    Shortcut Files
    Drag&Drop Menu
    Shell Change Notify
 
 
Documents
    Articles Index
    Press Releases
 
Purchase
    Buy now
    Resellers network
    Resellers wanted
 
Support
    Contact directly
    Customer's area
 
About/Contacts
    Shell+ Developers

Embarcader Technology Partner

 
Namespace extensions Mega-Demo Example

Mega-Demo example was developed as prototype project to one of our customers and implements many useful and commonly used features like:

  • Virtual Namespace
  • Login dialog
  • Custom detailed columns
  • Clipboard and Drag&Drop operations for files and folders
  • System icons for files and folders inside the namespace
  • Displaying overlays for icons
  • Custom property pages and native security page support
  • Context menus support
  • Explorer toolbar buttons
  • Main Explorer menu

This article will explain all stages of this project creation.

Quick links:

Creating the core

The core of any namespace extension is a ActiveX Library project, main TSxModule and main TPermanentModule. This modules created using New... command from Delphi's File menu.

MainModule represents interface between your NSE and Windows Shell. It's instance created almost for every call from Windows Shell and you will try to make its implementation so "light" as possible.

PermanentModule represents your NSE DataModule. It created once per Explorer process and will contain implementation of all "heavy" and long-time operations.

Also you can learn Shell+ MegaDemo NSE object-model, which were produced using Model Maker:


Click on the picture to enlarge it.

Only three basic components required to create simple namespace extension: ShellFolder, ShellView and DataProvider. There are several kins of each component available, you can use any combinations of them depending on what you need. Also, feel free to contact us if you need custom data providers for any thirdparty data types or custom shell views.

As ShellFolder component in this example we will use TSxVirtualFolder. This component allows your namespace extension to be installed as virtual folder like My Documents, Control Panel, Network Places and so on. Our namespace extension will be installed under the My Computer folder. We have to set some important properties for this component:

Attributes Attributes.Folder = True
Attributes.HasSubfolder = True
Attributes.CanDelete = False
Attributes.CanRename = False
Attributes.HasPropSheet = True
Attributes.FileSystem = False
Attributes.FileSysAncestor = True
Attributes.Browsable = True
Attributes.DropTarget = True
DataProvider We'll specify the data provider later
JunctionFolder jpMyComputer (the root of this demo will be My Computer folder)
RefreshWhenRegistered True
ShellView We'll specify the ShellView later
URLHandler Active = True
CLSID = any unique GUID
URLIdentifier = [test]
WantsForParsing True

As ShellView we'll use several different components, but on the first time we'll put the TSxSysShellView on the MainModule and link it with ShellFolder.ShellView property. There are no important properties on the first time for this component.

This demo will work with custom data format, and we will use Microsoft Compound File Implementation to store its data. Data provider to work with this data format is not implemented, but it's not a problem with TSxByHandProvider component. It allows you to handle any data format with your own algorithm.

Put the TSxByHandProvider on the PermanentModule and add Permanent module's unit name to uses section of MainModule unit. There are many important properties and events for DataProvider component, but we will specify them later.

Connect newly created PermanentModule.DataProvider to MainModule.VirtualFolder.DataProvider property. This is the most important relation in the NSE - connecting Windows Shell interface with the data.

Additional stuff

To make the namespace extention more user-friendly we have to specify the icon and property page for the root folder. Use TSxExtractIcon and TSxShellPropSheetExt components to do it. This components correspondingly linked to ShellFolder's properties ExtractIconHandler and PropertySheetHandler.

TSxExtractIcon contains only static icons to our namespace extensions, but you can show any icon dynamically depending on your NSE's state (like Service is online or Service is offline).

Root property page will be used to choose database file to work with. To implement the root property sheet we have to create new property sheet page (using New... command from Delphi's File menu) and link it to the PropertySheet component. Read more about handling Property Page events in the Property Sheet development guide.

The Data Storage

The DataProvider component works like mediator between your namespace extension and Windows Shell. You can read more about data provider and its events in the article "Writing data handling events for DataProvider". In this project we providing folders structure to data provider using the OnPopulate event.

To make things much closer to reality our database implemented as independed server. We have one connection to storage file and this file can be specified using Properies dialog for our root folder. MDServer is a property to provide access to remote COM server.

There is one more reason to use independent server to keep and handle data: your namespace extension works inside of different applications and "eat" system resources. So it should be a "thin client" which can only display data and handle user input.

This demo interact with its server through COM interfaces. This is one of possible approaches. The often used thirdparty communication solutions are: RemObjects, MsgConnect, Indy, Synapse Sockets.

The code to populate our virtual items will look like:

procedure TPermanentModule.DataProviderPopulate(Sender: TSxByHandProvider;
  DataGrain: TSxByHandDataGrain; GrainList: TList);
var
  tempGrain:TSxCustomDataGrain;
  grainString:String;
  stgFileObj:IMDFileObj;
  i:Integer;
  tempStr:TStrings;
  tempWideString:WideString;
  tempStgObj:IMDFileObj;
begin
  if not LoggedIn then
  begin
    if not DoLogin then
       Exit;
  end
  else
    SetLogoutTime;
  if DataGrain.IsRoot then // DataGrain is a folder, that need to be populated
  begin
    // Creating virtual folder (it will display files)
    tempGrain:=Sender.CreateGrainInstance;
    tempGrain.Name:='Stored Files';
    tempGrain.GrainType:=gtFolder;
    tempGrain.Strings.Values[vGrainPath]:=folderID_Files;
    tempGrain.Strings.Values[vSysIconOnly]:='1';
    GrainList.Add(tempGrain); // adding newly created item to the list
  end
  else
  begin
    grainString:=DataGrain.Strings.Values[vGrainPath]; // Enumerated grain path
    stgFileObj:=nil;
    if grainString=folderID_Files then
    begin
      if Failed(MDServer.GetFileStorage(stgFileObj)) then // Displaying files
         Exit;
    end
    else
    if grainString=folderID_TreeView then // Displaying file tree view
    begin
      Exit;
    end
    else
    if grainString=folderID_Grid then // Displaying data in grid
    begin
      Exit;
    end
    else
    begin // working with some file grain (the string is a path)
      if Failed(MDServer.GetRelativeFileStorage(grainString,stgFileObj)) then // Displaying files
         Exit;
    end;
    if stgFileObj=nil then
       Exit;
    tempStr:=TStringList.Create;
    try
      stgFileObj.GetChildNames(tempWideString); // retreiving the list of childs
      tempStr.Text:=tempWideString; // to make things easy...
      for i:=0 to tempStr.Count-1 do
      begin
        stgFileObj.GetChildByName(tempStr[i],tempStgObj); // retreiving child's object
        if tempStgObj=nil then
           Continue;
        tempGrain:=CreateGrainFromStg(tempStgObj,DataGrain);
        GrainList.Add(tempGrain);
      end;
    finally
      FreeAndNil(tempStr);
    end;
  end;
end;

It is only complicated method in the Namespace extension written with Shell+. As incoming parameter it receive DataGrain. You have to fetch all underlying DataGrains and add them to the GrainList param.

This event called every time when Windows Shell or any other application trying to get contents of any of your virtual folders, which specified by DataGrain param.

 

Implementing Login dialog

As you can see, OnPopulate event call several methods (LoggedIn, DoLogin) to check if user has logged in. DoLogin method will display dialog to ask user for login and password. Also you can use TSxVirtualFolder.OnGetShellView event to switch to another shellview when users has not logged in. For example you can switch to TSxShellFormView, which will display login dialog embedded to Explorer. This is currently not implemented in this demo.

function TPermanentModule.DoLogin: Boolean;
var
  tempLoginForm:TLoginForm;
begin
  Result:=False;
  tempLoginForm:=TLoginForm.Create(Self);
  try
    if tempLoginForm.ShowModal=mrOk then
    begin
      LoggedIn:=True;
      Result:=True;
    end
    else
      DoLogout;
  finally
    FreeAndNil(tempLoginForm);
  end;
end;

function TPermanentModule.GetLoggedIn: Boolean;
var
  tempTime:TTimeStamp;
begin
  Result:=FLoggedIn;
  if Result=True then
  begin
    tempTime:=DateTimeToTimeStamp(Time);
    Result:=tempTime.Date=LogoutTime.Date;
    if Result then
       Result:=tempTime.Time<=LogoutTime.Time;
    FLoggedIn:=Result;
  end;
end;

Implementing detail columns

To provide information in detailed columns to Windows Explorer we use TSxNamespaceDetails component. Put it on the MainModule and connect it to SysShellView.Details property.

Next step you have to specify the list of columns, which will be displayed in your NSE. Add columns to SxNamespaceDetails.Columns property. In our example it is three columns: Name, Size and Path.

Name and Size columns are commonly used columns, so we will register them is additional properties:

Path is additional column, which can be displayed by user by choosing Explorer menu View -> Choose Details...:

 

Clipboard and Drag&Drop operations

To allow your NSE to support clipboard and Drag&Drop operations you need to implement several DataProvider events. To get more information about Data Handling events please refer to article "Writing data handling events for DataProvider".

When user drop folder into the your namespace extension, DataProvider automatically parse incoming data structure and raise appropriate events for dropped folder, its subfolders and files inside.

This demo will show you how to create new DataGrains from files and folders which were dropped to NSE folder.

To handle incoming files we creating two events: OnNewGrainFromFile and OnNewGrainFromStream:

procedure TPermanentModule.DataProviderNewGrainFromFile(
  Sender: TSxCustomProvider; FolderGrain: TSxCustomDataGrain;
  FileName: WideString;var NewGrain:TSxCustomDataGrain);
var
  tempFileStg:IMDFileObj;
  newFileObj:IMDFileObj;
  fileStream:TFileStream;
  tempVar:OleVariant;
begin
  // retreiving file container from database (Folder in this case)
  MDServer.GetRelativeFileStorage(FolderGrain.Strings.Values[vGrainPath],tempFileStg);
  if tempFileStg=nil then
     Exit;
  // adding new file
  tempFileStg.AddNewChild(ExtractFileName(FileName),newFileObj);
  if newFileObj=nil then
     Exit;
  // specifying file attributes
  newFileObj.ReadOnly:=False;
  newFileObj.IsSystemIcon:=True;
  newFileObj.Kind:=okFile;
  // loading data to database
  fileStream:=TFileStream.Create(FileName,fmShareDenyNone or fmOpenRead);
  try
    tempVar:=SxStreamToVarArray(fileStream);
    if not VarIsNull(tempVar) then
       newFileObj.SetContents(tempVar);
  finally
    FreeAndNil(fileStream);
  end;
  // updating changes into database
  newFileObj.UpdateChanges;
  // updating ShellView
  Sender.NotifyUpdateGrain(FolderGrain);
  // returning representation of database object
  NewGrain:=CreateGrainFromStg(newFileObj,FolderGrain);
end;

procedure TPermanentModule.DataProviderNewGrainFromStream(
  Sender: TSxCustomProvider; FolderGrain: TSxCustomDataGrain;
  FileInfo: TSxFileInfoDescriptor; FileData: TStream);
var
  tempFileStg:IMDFileObj;
  newFileObj:IMDFileObj;
  tempVar:OleVariant;
begin
  MDServer.GetRelativeFileStorage(FolderGrain.Strings.Values[vGrainPath],tempFileStg);
  if tempFileStg=nil then
     Exit;
  tempFileStg.AddNewChild(FileInfo.FileName,newFileObj);
  if newFileObj=nil then
     Exit;
  newFileObj.ReadOnly:=ftReadOnly in FileInfo.FileAttributes;
  newFileObj.IsSystemIcon:=True;
  newFileObj.Kind:=okFile;
  tempVar:=SxStreamToVarArray(FileData);
  newFileObj.SetContents(tempVar);
  newFileObj.UpdateChanges;
  Sender.NotifyUpdateGrain(FolderGrain);
end;

These events are almost similar: first of all this event retrieve storage object by FolderGrain, then it save information from file or stream to the server.

To handle incoming folders we writing event handler for DataProvider.OnCreateNewFolder event:

procedure TPermanentModule.DataProviderCreateNewFolder(
  Sender: TSxCustomProvider; OwnerFolderGrain: TSxCustomDataGrain;
  NewFolderName: WideString; out NewFolderGrain: TSxCustomDataGrain);
var
  tempFileStg:IMDFileObj;
  tempNewStg:IMDFileObj;
begin
  MDServer.GetRelativeFileStorage(OwnerFolderGrain.Strings.Values[vGrainPath],tempFileStg);
  if tempFileStg=nil then
     Exit;
  tempFileStg.AddNewChild(NewFolderName,tempNewStg);
  NewFolderGrain:=CreateGrainFromStg(tempNewStg,OwnerFolderGrain);
end;

To allow our virtual files to be copied to another folders and applications we implementing two events: OnGetGrainFileContents and OnGetGrainFileInfo:

procedure TPermanentModule.DataProviderGetGrainFileInfo(
  Sender: TSxCustomProvider; DataGrain: TSxCustomDataGrain;
  var FileInfo: TSxFileInfoDescriptor);
var
  tempFileStg:IMDFileObj;
begin
  MDServer.GetRelativeFileStorage(DataGrain.Strings.Values[vGrainPath],tempFileStg);
  if tempFileStg=nil then
     Exit;
  FileInfo.FileAttributes:=DataGrain.Attributes;
  FileInfo.CreationTime:=Time;
  FileInfo.LastAccessTime:=Time;
  FileInfo.LastWriteTime:=Time;
  FileInfo.FileSize:=tempFileStg.Size;
  FileInfo.FileName:=tempFileStg.Name;
end;

procedure TPermanentModule.DataProviderGetGrainFileContents(
  Sender: TSxCustomProvider; DataGrain: TSxCustomDataGrain;
  Stream: TMemoryStream);
var
  tempFileStg:IMDFileObj;
  tempVar:OleVariant;
begin
  MDServer.GetRelativeFileStorage(DataGrain.Strings.Values[vGrainPath],tempFileStg);
  if tempFileStg=nil then
     Exit;
  if Failed(tempFileStg.GetContents(tempVar)) then
     Exit;
  SxVarArrayToStream(tempVar,Stream);
end;

OnGetGrainFileInfo event used to provide system-related information to filesystem or other application, and OnGetGrainFileContents event used to specify virtual file contents.

When user drag folder outside from your namespace extension DataProvider automatically handle folder information and raise only file-related events.

 

System and custom icons for files and folders inside virtual namespace

Our namespace extension will use common system icons to display its files. It is very easy to implement since 2.3 version:

procedure TPermanentModule.DataProviderPrepareGrainApperance(
  Sender: TSxCustomProvider; DataGrain: TSxCustomDataGrain;
  var IconParams: TSxPrepareIconParams);
begin
  IconParams.UseSystemIcon:=DataGrain.Strings.Values[vSysIconOnly]='1';
end;

But this demo allows to customize its virtual icons:

So you have to implement additional event, which load customized icon from server and provide it to Windows Shell:

procedure TPermanentModule.DataProviderGetIcon(Sender: TSxCustomProvider;
  DataGrain: TSxCustomDataGrain; var IconIndex, OpenIconIndex: Integer);
var
  tempFileStg:IMDFileObj;
  tempIcon:TIcon;
  tempStream:TMemoryStream;
  tempVar:OleVariant;
begin
  if DataGrain.Strings.Values[vSysIconOnly]='1' then
  begin
    IconIndex:=SxGetFileSystemIcon(DataGrain.Name,False,DataGrain.GrainType=gtFolder);
    if DataGrain.GrainType=gtFolder then
       OpenIconIndex:=SxGetFileSystemIcon(DataGrain.Name,True,DataGrain.GrainType=gtFolder);
  end
  else
  begin
    IconIndex:=0;
    OpenIconIndex:=0;
    tempIcon:=TIcon.Create;
    tempStream:=TMemoryStream.Create;
    try
      MDServer.GetRelativeFileStorage(DataGrain.Strings.Values[vGrainPath],tempFileStg);
      if tempFileStg=nil then
         Exit;

      // retreiving normal icon image and adding it to system image list
      tempFileStg.GetIcon(tempVar);
      SxVarArrayToStream(tempVar,tempStream);
      tempStream.Position:=0;
      IconIndex:=DataProvider.SmallIcons.AddIconFromStream(tempStream);
      tempStream.Position:=0; // large icons index is the same as small icon index
      DataProvider.LargeIcons.AddIconFromStream(tempStream);

      // retreiving open icon image from database and adding it to system image list
      tempStream.Position:=0;
      tempFileStg.GetOpenIcon(tempVar);
      SxVarArrayToStream(tempVar,tempStream);
      tempStream.Position:=0;
      OpenIconIndex:=DataProvider.LargeIcons.AddIconFromStream(tempStream);
      tempStream.Position:=0; // large icons index is the same as small icon index
      DataProvider.LargeIcons.AddIconFromStream(tempStream);
    finally
      FreeAndNil(tempIcon);
      FreeAndNil(tempStream);
    end;
  end;
end;

 

Displaying overlays for icons in the NSE

To display overlay icons for your virtual files you have to put two TSxIconList components on the permanent module and connect them to DataProvider.LargeOverlayIcons and DataProvider.SmallOverlayIcons. These two lists will contain one and same icon in small and large sizes.

Then you need just implement one event handler and your NSE will display overlay icon depending on file state:

procedure TPermanentModule.DataProviderGetOverlayIndex(
  Sender: TSxCustomProvider; DataGrain: TSxCustomDataGrain;
  var OverlayIndex: Integer; var DisplayOverlay: Boolean);
begin
  OverlayIndex:=0;
  if ftReadOnly in DataGrain.Attributes then
     DisplayOverlay:=True
  else
     DisplayOverlay:=False;
end;

 

Property pages for virtual files and folders

TSxNamespacePropertySheet component used to add property pages to the NSE. Put this component on PermanentModule and connect it to the DataProvider.PropertySheets property. Then you have to create new property page form (using New... command from Delphi's File menu) and link it to the TSxNamespacePropertySheet component (PropertySheets property).

From property page form you can use any available methods of PermanentModule, for example - this is method, which read current DataGrain settings from server using PermanentModule properties:

procedure TGrainPropSheetForm.LoadProperties;
var
  tempFileStg:IMDFileObj;
  tempGrain:TSxCustomDataGrain;
  tempServer:IMDServer;
begin
  tempServer:=PermanentModule.MDServer;
  tempGrain:=TSxNamespacePropertySheet(PropSheetComponent).DataGrain;
  tempServer.GetRelativeFileStorage(tempGrain.Strings.Values[vGrainPath],tempFileStg);
  if tempFileStg=nil then
     Exit;
  edObjName.Text:=tempFileStg.Name;
  edtObjsCount.Text:=IntToStr(tempFileStg.ChildCount);
  edtSize.Text:=SxFormatSizeStr(IntToStr(tempFileStg.Size div 1024));
  cbReadOnly.Checked:=tempFileStg.ReadOnly;
  tempGrain.GetCurrentIcon(imgIcon.Picture.Icon,is32x32,False);
  if tempFileStg.IsSystemIcon then
  begin
    btnUseSystem.Enabled:=False;
    btnChangeIcon.Enabled:=True;
  end
  else
  begin
    btnUseSystem.Enabled:=True;
    btnChangeIcon.Enabled:=False;
  end;
end;

PropSheetComponent is a property of TSxShellPropSheetForm. This event handler retrieve current DataGrain and load its data from server.

When user click Apply or Ok button, event FileObjPropSheet.OnApply or OnApplyEx will be called. By handling this event you have to store modified data from property page to server:

procedure TPermanentModule.FileObjPropSheetApplyEx(
  Sender: TSxShellPropSheetExt; PropSheet: TForm;
  var ApplyChanges: Boolean);
begin
  TGrainPropSheetForm(PropSheet).SaveProperties;
  ApplyChanges:=True;
end;

 

Context menus support

DataProvider component have several properties and events to implement flexible context menus for Namespace extensions. Put the TSxGrainMenu component on the PermanentModule and connect it to DataProvider.FolderContextMenu property. This menu will be displayed only for folders in your Namespace extension. You can control what menu to display for specific DataGrain by handling DataProvider.OnGetFileContextMenu and DataProvider.OnGetFolderContextMenu events.

Common menu items like Cut, Copy, Paste, Delete and Rename will be added automatically when you implement data handling events for DataProvider component.

 

Explorer toolbar support

Drop the TSxExplorerToolbar component on the MainModule and two TImageList components on permanent module. These imagelists will contain large and small button images.

Then you will add one or more buttons to the ExplorerToolbar.Buttons property. Each button will be linked with TMenuItem on the PermanentModule.

Also you can choose between one of common toolbar images or any of your custom using Image and ImageIndex properties for each button.

 

Main Explorer menu support

Main Explorer menu extended using TSxExplorerMenu component. As like as with toolbar, you have to drop the TSxExplorerMenu component on the MainModule and specify any number of menu items. Currently you can extend only the following Explorer menus: File, Edit, View, Tools and Help.

Each menu item, which were added to TSxExplorerMenu component will be connected with TMenuItem on the PermanentModule. You can use one and the same TMenuItem with Explorer menu item and Toolbar item.

This demo extends File and Tools menu items of Windows Explorer:

Download Example

Delphi XE7 (x64)
NSE-MegaDemo-D21X64.zip
1.52Mb
3.11.289.357
19.09.2014
Delphi XE7
NSE-MegaDemo-D21.zip
335.55Kb
3.11.289.357
19.09.2014
Delphi XE6 (x64)
NSE-MegaDemo-D20X64.zip
1.53Mb
3.10.287.331
16.09.2014
Delphi XE6
NSE-MegaDemo-D20.zip
335.09Kb
3.10.287.331
16.09.2014
Delphi XE5 (x64)
NSE-MegaDemo-D19X64.zip
1.48Mb
3.10.287.331
16.03.2014
Delphi XE5
NSE-MegaDemo-D19.zip
332.81Kb
3.10.287.331
16.03.2014
Delphi XE4 (x64)
NSE-MegaDemo-D18X64.zip
1.47Mb
3.10.287.331
16.03.2014
Delphi XE4
NSE-MegaDemo-D18.zip
332.73Kb
3.10.287.331
16.03.2014
Delphi XE3 (x64)
NSE-MegaDemo-D17X64.zip
1.45Mb
3.10.287.331
16.03.2014
Delphi XE3
NSE-MegaDemo-D17.zip
332.51Kb
3.10.287.331
16.03.2014
Delphi XE2 (x64)
NSE-MegaDemo-D16X64.zip
1.19Mb
3.10.287.331
16.03.2014
Delphi XE2
NSE-MegaDemo-D16.zip
331.55Kb
3.10.287.331
16.03.2014
Delphi XE
NSE-MegaDemo-D15.zip
323.74Kb
3.10.287.331
16.03.2014
Delphi 2010
NSE-MegaDemo-D14.zip
323.36Kb
3.10.287.331
16.03.2014
Delphi 2009
NSE-MegaDemo-D12.zip
249.12Kb
3.10.287.331
16.03.2014
Delphi 2007
NSE-MegaDemo-D11.zip
247.36Kb
3.10.287.331
16.03.2014
Delphi 2006
NSE-MegaDemo-D10.zip
246.99Kb
3.10.287.331
16.03.2014
Delphi 2005
NSE-MegaDemo-D9.zip
249.42Kb
3.10.287.331
16.03.2014
Delphi 7
NSE-MegaDemo-D7.zip
249.72Kb
3.11.289.357
21.09.2014
Delphi 6
NSE-MegaDemo-D6.zip
251.35Kb
3.8.287.331
09.05.2012

MegaDemo installation guide

  1. Compile MegaDemo server and MegaDemo NSE projects.
  2. Register MegaDemo NSE using command "regsvr32.exe ExMegaDemoNSE.dll"
  3. Register MegaDemo server using command "ExMegaDemoServer.exe -regserver"
  4. Open Windows Explorer and choose item Properties from "MegaDemo Virtual Folder" item, which will appear under the "My Computer" node.
  5. Click on the button "Create New Database" and specify file name for the new empty database. Also you can download sample MegaDemo database and choose it using "Browse..." button.
  6. After you click Ok in the Properties dialog of MegaDemo NSE you will be able to work with this demo.

Components | Download | Purchase | Support | About Us
Copyright © 2016 ALDYN Software. All rights reserved.
Copyright © 2001 - 2011 Shell+ Development Group. All rights reserved.