A namespace extension provides a way for you to define a new object that a browser, such as the Windows Explorer, can explore. The code you provide and the registry entries you make define the icon images and text that the user sees while viewing your data, as well as the menus, toolbars, and status information the user can use on your data objects.
One of the reasons it is relatively simple to extend the shell's namespace is that the Explorer can be viewed as two independent parts: the browser code and the system namespace provider code. Because they were written polymorphically and communicate by using COM-based interfaces, they do not depend on each other's implementation. Thus, anyone can provide either browser code that browses the system namespace, or a namespace extension that extends the system namespace that can be browsed using the Explorer.
Your extension has to provide the Explorer with icons, names, and details you want to represent the items in your namespace. It can provide the Explorer with a custom context menu for your data, and drag-and-drop capabilities. A key thought to remember, however, is that the contents of your namespace are known only to you so the Explorer can work only with what you provide.
Structuring a Namespace Extension
As is the case with shell extensions, a namespace extension is implemented as a COM in-process server DLL. For even the simplest of extensions, experience with COM, OLE, and the behavior of the Windows Explorer is required before you attempt to implement a namespace extension. It also means that you must properly enter it in the system Registry or it will not work.
As a reminder, the construction of an in-process server DLL requires the implementation of a DLL that exports the following functions:
· DllMain
· DllGetClassObject
· DllUnloadNow
The DLL also implements an object that exposes IClassFactory for the creation of the other objects contained in the DLL. Those objects will expose IUnknown and the other interfaces necessary to implement a namespace extension, including IPersistFolder, IShellFolder, IEnumIDList, and IShellView. These interfaces allow the Explorer and your extension to display, interact, and communicate.
It is important to remember that the Explorer calls into your extension by using the IShellFolder and IShellView interfaces you have implemented in your extension. The IShellBrowser interface (implemented by the Explorer), allows your extension to call back into the Explorer. It is very similar to "Site"-type interfaces commonly found in OLE scenarios.
Beyond these required interfaces, your extension will need to implement other interfaces that will be created by the IShellFolder object. These include IExtractIcon to provide icons, IContextMenu to provide context menus for your items, drag-and-drop interfaces (IDropSource and IDropTarget), and IDataObject for data transfer.
Examples of in-process server DLLs, especially any that contain namespace or shell extensions, are obviously helpful in learning to build namespace extension DLLs.
Identifying Items in the Namespace
One of the operations that must be handled when extending the shell's namespace is the enumeration of items. The IShellFolder::EnumObjects method returns an enumerator object, IEnumIdList that will return a set of identifiers that identifies each item within a specific folder.Called a shell item IDList, it is an array of bytes that contains enough data to identify items by the parent folder. Only the first two bytes are defined (as the size of the ID) and the rest is opaque to the caller.
A shell item IDList must contain sufficient information to identify an object with a folder, but it may also contain additional information for efficient manipulation (such as retrieving display name or sorting). You have the option to store additional information because the Explorer does not compare two item IDs directly for either sorting or identification. Instead it uses IShellFolder::CompareID to perform this task.
Nonrooted and Rooted Explorers
Your namespace extension can be implemented in either of two ways and there is no set criteria for determining which to use. Rather, it depends only on your evaluation of which is more logical and better suited to your particular application.
You can implement your extension so the user can browse into it using the standard Explorer. In this case, your new namespace is presented as a sub-namespace to the system namespace already there. Since the Desktop is the root folder of the system's namespace, it also serves this purpose for your extension. Accordingly, your extension resides within the existing hierarchy of objects on the desktop and appears to the user as just another item in the system namespace.
On the other hand, if you analyze your application and determine that a completely separate namespace makes more sense, you can choose to implement your extension in just this way. However, the user will not be able to browse into it without running a special instance of the Explorer rooted in an item of your choosing. The rooted Explorer's top level is referred to as a junction point. It can be a file or a folder but if the extension uses anything as a junction point, it is by definition "rooted" because the Explorer does not support exploring directly into files.
As noted earlier, whether you choose to implement your extension as rooted or nonrooted is largely situational. There is no hard-and-fast rule. If your extension logically blends into the existing hierarchy of objects, a nonrooted Explorer might be best. If not, it will probably be better to implement a rooted Explorer with a specific file as your entry point to the new namespace,
Creating a Junction Point
Creating a junction point can be done in several different ways, depending on the item you choose for the junction point, such as a file or directory. For example, to make a junction point in either the Desktop or in the My Computer folder, add the following key to the registry:
HKEY_LOCAL_MACHINE
SOFTWARE
Microsoft
CurrentVersion
Explorer
[MyComputer
or Desktop]
{CLSID}
You can also use a directory as your junction point. If your operating system supports long filenames, you can use the CLSID of your namespace extension as the file extension of a folder (MyFolder.{20D....}. Otherwise, you can create a directory, change its file attributes to read-only and place a file called DESKTOP.INI into it. This simple text file is made up of the following:
[.ShellClassInfo]
CLSID={clsid}
Opening a Rooted Explorer
To open a rooted Explorer for the namespace you have created, you must provide a way to start the new instance of EXPLORER.EXE, using the /root switch on the command line. There are several ways to accomplish this. For example, you can either call ShellExecute directly, or you can create a shortcut file that contains one of the following as a command line:
· If the junction point is an item under the desktop:
explorer.exe /e,/root,::{CLSID of item}
· If the junction point is an item under My Computer:
explorer.exe /e,/root,,::{20D04FE0 - 3AEA - 1069 - A2D8 - 08002B30309D}\::{CLSID of item}}
· If the junction point is a file system folder:
explorer.exe /e,/root, [path to a junction point]
When the Explorer is opened using the /root::{CLSID} option, it sets the junction point object as the root of hierarchy and calls its IShellFolder.
When the user opens a junction point object or one of its subfolders, the Explorer lets it create a view object by calling IShellFolder::CreateViewObject and requesting an IShellView interface. The Explorer then calls IShellView::CreateViewWindow to allow it to create the view window of its folder. One of the parameters passed is a pointer to the IShellBrowser interface which allows the extension to communicate with the Explorer. The view object is able to add menu items to the menu bar, add toolbar buttons, display status information on the status bar, and/or process shortcut keys.
UI Negotiation (Menu, Toolbar, and Status Bar)
The mechanism to determine which items will appear in the view window while the contents are visible is similar to OLE in-place activation but notable differences do exist. Three of them are discussed here.
First, the view window always exists even though it does not have the input focus. Therefore, it should maintain three states:
· Deactivated
· Activated with the focus
· Activated without the focus
The view window may present different sets of menu items depending on the focus state. The Explorer notifies the state changes by calling IShellView::UIActivate. The view object should call IShellBrowser::OnViewWindowActivate when the view window is activated by the user.
Second, the Explorer does not support layout negotiation. Instead, it allows the view window to add toolbar buttons or set status bar texts. The view window may create modeless popups. The view object may call IShellBrowser::GetControlWindow or IShellBrowser::SendControlMsg to control them. The Explorer forwards appropriate notification messages from those controls using IShellView::ForwardControlMsg.
Third, the Explorer allows the view window to add menu items to the Explorer's pull-down menus (in addition to inserting top-level pull-down menus). In other words, the view object is allowed to insert menu items to submenus returned from IShellBrowser::InsertMenus. To let the Explorer dispatch menu messages correctly, a certain range of menu item IDs (between SHVIEW_FIRST and SHVIEW_LAST) must be used.
Persistent View State
The Explorer defines a set of standard view states:
· View mode, such as large/small icon view (or detail view)
· View attributes, such as snap to grid.
The Explorer provides a persistent medium to store these states and though using them is not required, it is recommended. The setting is passed to the view object by using IShellView::CreateViewWindow and retrieved from it by using IShellView::GetCurrentInfo.
The Explorer also provides a persistent medium (a stream) to let the view object store view-specific information (such as scroll positions or icon layout). The view object can access this stream by calling IShellBrowser::GetViewStateStream.
When the user is browsing from one folder to another, the Explorer passes the pointer to the previously viewed IShellView instance as a parameter to IShellView::CreateViewWindow (before calling its DestroyViewWindow). This allows the next view object to retrieve appropriate view state from the previous view object (such as column widths of its details view), typically by calling IUnknown::QueryInterface on a private interface.
Registering Your Namespace Extension
Registering your extension is not particularly difficult but it must be done precisely or users will never find your namespace extension. Consider the following registry entries, some of which are required for any namespace extension.
Namespace extensions, like all COM objects, must be registered in the CLSID section. In addition, they also have to be "apartment aware", having the named value ThreadingModel set to Apartment, as shown below.
HKEY_CLASSES_ROOT\CLSID\{CLSID}
HKEY_CLASSES_ROOT_CLSID\{CLSID}\InProcServer32\(default)=path\filename.dll
HKEY_CLASSES_ROOT_CLSID\{CLSID}
\InProcServer32\"ThreadingModel"
= "Apartment"
If your namespace extension is going to run under a rooted Explorer, you should add the following entries so the Open and Explorer verbs appear on your namespace extension.
HKEY_CLASSES_ROOT_CLSID\{CLSID}\Shell\Open\Command\(default)=c:\windows\explorer.exe/root,%1
HKEY_CLASSES_ROOT_CLSID\{CLSID}\Shell\Explore\Command\(default)=c:\windows\explorer.exe/e,/root/%1
You can also include entries under the following key for the default icon.
HKEY_CLASSES_ROOT_CLSID\{CLSID}\DefaultIcon\(default)="path\filename.dll",IconIndex
The following is an optional key but one that can be very important. If you include the "Attributes" named value under ...{CLSID}\ShellFolder, it specifies the attributes of a junction point by using the SFGAO_* flags returned by the IShellFolder::GetAttributesOf method.
HKEY_CLASSES_ROOT_CLSID\{CLSID}\ShellFolder\Attributes = 0000 00 00
For example, if the SFGAO_FOLDER flag is set and its junction point exists in the system namespace, the user sees your extension's icon in the Explorer's left pane and is able to browse into it using the standard (nonrooted) Explorer. If this flag is not set, you must provide a rooted Explorer for browsing
Another example is the SFGAO_HASSUBFOLDER flag. If it is set, the Explorer can make the extension's icon expandable to the next level by displaying the + icon to its left.
Other registry entries you should know about include those required for putting items into MyComputer or on the Desktop. The following entries list all the items in those two locations:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace
Finally, the following entry is currently required only for Windows NT but it should always be included for future compatibility. This is the list of extensions that the shell will load. Extensions not on this list will not be loaded by the shell on NT.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\ShellExtensions\Approved\{CLSID}="Extension Name"
Additional Namespace Extension Considerations
The material in this overview is specifically intended for the beta 2 release of Windows NT 4.0 (SUR). It is not final, nor is it complete. Additional information will be provided in future releases of this product.
Additional interfaces not discussed here include IShellExecuteHook, IShellIcon, and ICommDlgBrowser.