 |
I |
 |
 |
tem identifiers and item identifier lists are what the shell
uses to identify items in the its namespace. An item |
 |
 |
 |
xxxxxxxxxxxxxxx |
identifier is much like a filename, and is unique to its parent
folder. An item identifier list (IDL) is like a full pathname - it
traces the path from the desktop to the item.
The only thing that is documented about an item identi-
fier is the size field in the first two bytes. The format of the
rest of the structure is dependent on the parent folder. An
IDL (defined by the ITEMIDLIST structure) is really just a
bunch of item identifiers joined together and terminated
with a 16-bit zero. Most shell functions require or return a
pointer to an IDL (a PIDL).
All of this is explained in more detail in the API help and
is basically all you need to know in order to manipulate
IDLs. There is no real need for most of the undocumented
functions described in this section. However, working with
variable length, byte-aligned structures can be rather
messy, so it's convenient to have the shell do most of the
work for you. |
 |
Memory Allocation |
 |
The first trick in dealing with IDLs is that the memory al-
most always has to be allocated and released using the
shell's memory allocator (an IMalloc interface retrieved
using the SHGetMalloc function). When exchanging PIDLs,
it is often the responsibility of the application to free a PIDL
created by the shell and vice versa. It is therefore important
that you're both using the same memory allocator.
Fortunately there are two simple functions, SHAlloc
and SHFree, which can allocate and release memory using
the shell allocator, without the effort of obtaining an IMalloc |
interface. Like all the
functions in this sec-
tion, they're exported
from SHELL32.DLL and
they're exported by or-
dinal (ordinals 196 and |
 |
 |
LPVOID WINAPI SHAlloc(ULONG cb);
void WINAPI SHFree(LPVOID pv);
void WINAPI ILFree(LPCITEMIDLIST pidl); |
Figure 1 Memory Related Functions |
 |
|
|
195 respectively). The functions declarations are shown in
Figure 1.
When freeing a PIDL, though, it is probably preferable
to use the ILFree function (ordinal 155), even if it's not
much more than a wrapper around SHFree. It does also
make sure the PIDL isn't NULL before calling SHFree, but I
would expect the IMalloc implementation to take care of
that anyway. The declaration for ILFree can also be seen
in Figure 1. |
|
 |
|
|
 |
From Paths to PIDLs |
 |
Typically PIDLs are returned to you by certain functions and
passed around to other functions, without you ever really
knowing anything about the internal structure of the IDL and
without you ever needing to create one. Obviously if you're
implementing a namespace extension, you'll need to create
item identifiers for the objects in your own virtual folders,
but the format of those item identifiers is specific to your
folder and you can do whatever you want with them.
But what happens if you need to create a PIDL for
somebody else's namespace, like a path in the file system? |
The 'documented'
method would be
to get an IShell-
Folder interface
for the relevant
folder and then
call Parse-
DisplayName for
the name you
need to convert
into a PIDL. For a
filename, you
would typically |
 |
 |
HRESULT WINAPI SHILCreateFromPath(
LPCSTR lpszPath,
LPITEMIDLIST *ppidl,
ULONG *pdwAttributes);
LPITEMIDLIST WINAPI ILCreateFromPath(
LPCSTR lpszPath);
LPITEMIDLIST WINAPI SHSimpleIDListFromPath(
LPCSTR lpszPath); |
Figure 2 Creation Functions |
 |
|
|
use the desktop IShellFolder (SHGetDesktopFolder).
However, if you're feeling lazy, you might find it easier
to make use of one of the three functions in Figure 2.
SHILCreateFromPath is basically just a wrapper around
the desktop folder's ParseDisplayName, and ILCreate-
FromPath is just a simplified wrapper around SHILCreate-
FromPath. SHSimpleIDListFromPath, however, im-
plements the whole process itself. The ordinal values are
28, 157 and 162 respectively.
I'm not entirely sure what the difference is between the
two methods, but I suspect that SHSimpleIDListFromPath
is slightly faster but doesn't cache all the details that would
normally be stored in an item identifier. For example, if you
pass a PIDL created by SHSimpleIDListFromPath to the
SHBrowseForFolder function, you will notice that the dis-
play name and icon aren't always correct. I could be totally
wrong about the cause though. |
|
 |
|
|
 |
Parsing a PIDL |
 |
If, for some reason, you need to iterate through each item
identifier in an IDL, you will probably find ILGetNext very
useful. When given a PIDL (or a pointer to any item in the
IDL for that matter), the function will return a pointer to the
next item identifier in the IDL. If the PIDL is NULL or is
already pointing to the last item in the IDL, the function will
return NULL. For the specific case of finding the last item in
the IDL, you can just call ILFindLastID.
An even more specific form of search is the ILFind-
Child function. Given a parent PIDL and a child PIDL, it will |
return a pointer to the unique
portion of the child. For
example, if you passed it PIDLs
for 'C:\DIR' as the parent and
'C:\DIR\FILE.TXT' as the child, it
would return a pointer to the
portion of the child PIDL that
represents 'FILE.TXT'. If the
given child is not really a child
of the parent, the function will
return NULL.
The ordinals for these three |
 |
 |
LPITEMIDLIST WINAPI ILGetNext(
LPCITEMIDLIST pidl);
LPITEMIDLIST WINAPI ILFindLastID(
LPCITEMIDLIST pidl);
LPITEMIDLIST WINAPI ILFindChild(
LPCITEMIDLIST pidlParent,
LPCITEMIDLIST pidlChild); |
Figure 3 Parsing Functions |
 |
|
|
functions are 153, 16 and 24 respectively. The function
declarations can be seen in Figure 3. |
|
 |
|
|
 |
Copying and Combining |
 |
Something that is even more useful when dealing with
PIDLs, is the ability to make a copy of a PIDL passed to you
by the shell. When passed an existing PIDL, the ILClone
function will return a new identical copy of that PIDL. The
ILCloneFirst function, on the other hand, will return a new
PIDL containing only the first item identifier from the source
PIDL. If you need a copy of the last item identifier, you
could use a combination of ILFindLastID and ILCloneFirst.
For other portions of the IDL, you would have to use ILGet-
Next and ILCloneFirst.
If you want to combine two PIDLs together, you would
use the ILCombine function. Given two PIDLs, it will create |
a new PIDL containing the two
source IDLs joined together
consecutively. If you want to
combine a single item identifier
with a PIDL, you would use the
ILAppendID function. It can
be used to append an ITEMID
to either the beginning or the
end of an existing IDL.
However, unlike ILCombine,
the orginal PIDL is destroyed by
this operation. The ILAppend-
ID function can also be used to
create a PIDL from a item iden-
tifier alone, by passing a NULL
for the PIDL. |
 |
 |
LPITEMIDLIST WINAPI ILClone(
LPCITEMIDLIST pidl);
LPITEMIDLIST WINAPI ILCloneFirst(
LPCITEMIDLIST pidl);
LPITEMIDLIST WINAPI ILCombine(
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2);
LPITEMIDLIST WINAPI ILAppendID(
LPITEMIDLIST pidl,
LPCSHITEMID lpItemID,
BOOL bAddToEnd); |
Figure 4 Functions for Copying and Combining |
 |
|
|
The ordinals for these four functions are 18, 19, 25 and
154 respectively. The function declarations can be seen in
Figure 4. |
|
 |
|
|
 |
Death and Destruction |
 |
If you need to delete an entire PIDL, you would obviously
just use the ILFree funcion. If you need to remove the last |
item identifier from the end of an IDL
you would use the ILRemoveLastID
function (see Figure 5). The return va-
lue is TRUE if the operation was suc-
cessful. Note, however, that it doesn't |
 |
 |
BOOL WINAPI ILRemoveLastID(
LPITEMIDLIST pidl); |
Figure 5 ILRemoveLastID |
 |
|
|
actually free any memory - it just resets the end of list
marker. The ordinal value is 17.
Unfortunately that is the only delete function that
exists. If you want to remove an item identifier from the
beginning of an IDL, the best you can do is use a combina-
tion of ILGetNext and ILClone and then delete the original
PIDL with ILFree. Other delete operations would probably
be even more complicated, but I'm assuming they aren't all
that common. |
|
 |
|
|
 |
Equality and Parenthood |
 |
If you need to determine if two PIDLs are equal, you would
use the ILIsEqual function. If you need to determine if a
particular PIDL is a child of another PIDL, you would call the
ILIsParent function passing it the suspected parent and |
child. If you require that the child be
an immediate descendent (i.e. it is a
member of the parent folder, not a
member of a subfolder), then you
would set the last parameter of the
function to TRUE. See Figure 6 for the
function declarations.
Note that the equality of two IDLs
is not determined with a binary com-
parison. Both of the above functions
use the desktop folder's CompareIDs |
 |
 |
BOOL WINAPI ILIsEqual(
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2);
BOOL WINAPI ILIsParent(
LPCITEMIDLIST pidlParent,
LPCITEMIDLIST pidlChild,
BOOL bImmediate); |
Figure 6 Testing Functions |
 |
|
|
function to perform equality tests. The ILIsParent function
obviously only tests whether the 'base' PIDL of the child is
equal to the parent PIDL.
The ordinals for these functions are 21 and 23 respec-
tively. |
|
 |
|
|
 |
Saving and Loading |
 |
 |
HRESULT WINAPI ILSaveToStream(
LPSTREAM pstrm,
LPCITEMIDLIST pidl);
HRESULT WINAPI ILLoadFromStream(
LPSTREAM pstrm,
LPITEMIDLIST *ppidl); |
Figure 7 Streaming Functions |
 |
|
 |
I can't honestly think of any rea-
son for using the next two
functions, but if you ever need to
save or load a PIDL using
streams you would use the
ILSaveToStream and ILLoad-
FromStream functions (see
Figure 7). The ordinal values are
27 and 26 respectively. |
|
Note that when calling ILLoadFromStream, the PIDL
pointed to by the ppidl parameter must either be NULL or
must be a valid PIDL. The function will always attempt to
free the PIDL before overwriting it, which will obviously
have disastrous consequences if it is just a random uninitia-
lized value. |
|
 |
|
|
 |
Global Memory Allocation |
 |
As I mentioned earlier, the memory for an IDL should al-
most always be allocated using the shell's memory alloca-
tor. However, there are two functions that use a different
method of allocating and freeing memory, ILGlobalClone
and ILGlobalFree (ordinals 20 and 156). The function dec-
larations can be seen in Figure 8.
On Windows NT, these global functions just use the
default process heap (as returned by GetProcessHeap).
This led me to believe that heap allocations were in some |
way more efficient than the
shell allocator, and the global
functions were only used in-
ternally by the shell for rea-
sons of efficiency.
However, on Windows
95, most of the internal struc-
tures in the shell need to be |
 |
 |
LPITEMIDLIST WINAPI ILGlobalClone(
LPCITEMIDLIST pidl)
void WINAPI ILGlobalFree(
LPCITEMIDLIST pidl); |
Figure 8 Global Memory Functions |
 |
|
|
shared between all instances of the DLL. In the case of
PIDLs, the memory used when allocating them obviously
has to be shareable as well. ILGlobalClone solves this
problem by using an undocumented shared heap for the
allocations, conveniently making the pointers accessible
from anywhere. |
|
 |
|
|
 |
The Rest |
 |
If you need to determine the size of an IDL, you would use
the ILGetSize function. If you need to determine the display |
name of a PIDL you could either use
the ILGetDisplayName function, or
the documented SHGetPathFrom-
IDList. ILGetDisplayName basically
just calls the desktop shell folder's
GetDisplayNameOf function with
the flag SHGDN_FORPARSING. From
what I can make out, that eventually
leads to SHGetPathFromIDList |
 |
 |
UINT WINAPI ILGetSize(
LPCITEMIDLIST pidl);
BOOL WINAPI ILGetDisplayName(
LPCITEMIDLIST pidl,
LPSTR lpszName); |
Figure 9 ILGetSize and ILGetDisplayName |
 |
|
|
being called anyway, so there doesn't really seem to be any
need to use the undocumented function.
The ordinal values for ILGetSize and ILGetDisplay-
Name are 152 and 15 respectively. See Figure 9 for the
function declarations. |
|
 |
|
|
 |
Windows NT and Unicode Strings |
 |
One last thing I should mention. Undocumented functions
on Windows NT almost always expect string parameters to
be in Unicode. So anywhere you see an LPSTR or an
LPCSTR type, it should actually be LPWSTR or LPCWSTR
when running on Windows NT. |
 |
BACK TO HOME |