Discussion:
Selecting shell items
(too old to reply)
Mick
2004-01-05 23:19:03 UTC
Permalink
Hello All,

I've got a context menu (IContextMenu) shell extension and
I need to be able to programmatically set the selected
state of a collection of shell items (i.e. files). I have
the full paths of the files I want to select, but not the
PIDLs. Would somebody please tell me how to set the
selected state of these items?

Regards,
Mick
Jim Barry
2004-01-06 02:02:49 UTC
Permalink
Post by Mick
I've got a context menu (IContextMenu) shell extension and
I need to be able to programmatically set the selected
state of a collection of shell items (i.e. files). I have
the full paths of the files I want to select, but not the
PIDLs. Would somebody please tell me how to set the
selected state of these items?
Again, this is straying into the realm of the semi-documented, but see the thread "browse to another folder in explorer context menu shell extension" above. Once you have the IShellBrowser interface, you can call its QueryActiveShellView method to obtain the IShellView interface implementing the SelectItem method.

Alternatively, if you implement IObjectWithSite in your shortcut menu handler, your SetSite method receives an interface that can be queried for IServiceProvider. You can then query for the SID_SFolderView/IID_IFolderView service which allows you to call IFolderView::SelectItem. (The IFolderView can also be queried for IShellView.) I expect this only works from Windows 2000 onwards though.

For converting full filesystem paths into ID-lists there is SHParseDisplayName (XP only), or the desktop folder's IShellFolder::ParseDisplayName method (all platforms). Each filesystem folder can also parse paths relative to itself.
--
Jim Barry, MVP for Windows SDK
Mick
2004-01-06 02:44:29 UTC
Permalink
Jim,

I found some code that allowed me to get a handle to
Explorer's listview window, and I can select the item(s)
using ListView_SetItemState, but after my menu handler's
InvokeCommand function returns the view redraws itself and
the selection state of the items is somehow lost.

I'll give IShellView->SelectItem a try.

Thanks!

Mick
-----Original Message-----
Post by Mick
I've got a context menu (IContextMenu) shell extension
and
Post by Mick
I need to be able to programmatically set the selected
state of a collection of shell items (i.e. files). I
have
Post by Mick
the full paths of the files I want to select, but not
the
Post by Mick
PIDLs. Would somebody please tell me how to set the
selected state of these items?
Again, this is straying into the realm of the semi-
documented, but see the thread "browse to another folder
in explorer context menu shell extension" above. Once you
have the IShellBrowser interface, you can call its
QueryActiveShellView method to obtain the IShellView
interface implementing the SelectItem method.
Alternatively, if you implement IObjectWithSite in your
shortcut menu handler, your SetSite method receives an
interface that can be queried for IServiceProvider. You
can then query for the SID_SFolderView/IID_IFolderView
service which allows you to call IFolderView::SelectItem.
(The IFolderView can also be queried for IShellView.) I
expect this only works from Windows 2000 onwards though.
For converting full filesystem paths into ID-lists there
is SHParseDisplayName (XP only), or the desktop folder's
IShellFolder::ParseDisplayName method (all platforms).
Each filesystem folder can also parse paths relative to
itself.
--
Jim Barry, MVP for Windows SDK
.
Mick
2004-01-06 23:34:08 UTC
Permalink
Jim,

Okay, I got the IShellBrowser interface, then
QueryActiveShellView to get the IShellView interface, but
couldn't get that to work at all.

So, next I got the IShellFolder and IShellView interfaces
for the folder that my context menu extension is working
in. This allowed me to use IShellFolder::SetNameOf to
change the name(s) of the file(s), and it also allowed me
to use IShellView:: SelectItem to set the selected state
(s) of the renamed item(s). I added a Sleep statement just
before my InvokeCommand function returns and sure enough
the items are selected. But as soon as the InvokeCommand
function returns the selection states get screwed up the
same as before. What's really strange is that not all of
the selection states are lost, only about half of the
items I selected become unselected!!! I'm guessing that
the shell must be calling the view's Refresh function
after my context menu is displayed and that somehow messes
up the selection.

I tried spinning a thread and having it set the selection
after my InvokeCommand function returns, but the
destructor for my menu extension gets called before the
thread can finish which causes an exception.

Got any other ideas? <g>

Regards,
Mick
-----Original Message-----
Post by Mick
I've got a context menu (IContextMenu) shell extension
and
Post by Mick
I need to be able to programmatically set the selected
state of a collection of shell items (i.e. files). I
have
Post by Mick
the full paths of the files I want to select, but not
the
Post by Mick
PIDLs. Would somebody please tell me how to set the
selected state of these items?
Again, this is straying into the realm of the semi-
documented, but see the thread "browse to another folder
in explorer context menu shell extension" above. Once you
have the IShellBrowser interface, you can call its
QueryActiveShellView method to obtain the IShellView
interface implementing the SelectItem method.
Alternatively, if you implement IObjectWithSite in your
shortcut menu handler, your SetSite method receives an
interface that can be queried for IServiceProvider. You
can then query for the SID_SFolderView/IID_IFolderView
service which allows you to call IFolderView::SelectItem.
(The IFolderView can also be queried for IShellView.) I
expect this only works from Windows 2000 onwards though.
For converting full filesystem paths into ID-lists there
is SHParseDisplayName (XP only), or the desktop folder's
IShellFolder::ParseDisplayName method (all platforms).
Each filesystem folder can also parse paths relative to
itself.
--
Jim Barry, MVP for Windows SDK
.
Jim Barry
2004-01-07 00:54:15 UTC
Permalink
Post by Mick
Got any other ideas? <g>
Hmmm, not really... it seems to work pretty well for me. Maybe you should post some code so we can see what you're doing. Where is your handler registered?

- Jim
Mick
2004-01-09 23:22:59 UTC
Permalink
-----Original Message-----
Hmmm, not really... it seems to work pretty well for me.
Maybe you should post some code so we can see what you're
doing. Where is your handler registered?

Jim,

Are you saying that you're using IShellView::SelectItem
and it's actually working? As I said before, I am and it's
not working. I call SelectItem and pass it a folder
relative pidl and I get no error but the item is not
selected.

I could post the code since it's just a utility I wrote
for my own use, but it's pretty big and I'd need to pare
it down before I could post it. Please, do me a favor and
give me a sanity check with what I'm currently doing.

1. My context menu's Initialize function gets called.
The function uses the passed-in IdataObject to build a
vector of strings containing the full paths of the
selected items. The function also extracts the folder's
path using the path of the first selected item. The vector
of file paths and the folder path are saved as data
members in the class.
2. The user selects a menu command. In this
case "Rename All".
3. My menu's InvokeCommand function gets called.
InvokeCommand calls SHGetDesktopFolder and uses that
interface's ParseDisplayName to get the full pidl of the
folder saved in step 1. The folder pidl is then passed to
the desktop's IShellFolder::BindToObject function which
returns the IShellFolder for the folder. This
IShellFolder's CreateViewObject function is called to get
the IShellView for the folder. InvokeCommand then calls
the RenameAll member function of the context menu class.
4. The RenameAll function pops up a dialog which
allows the user to enter a base filename and set an option
to append either a sequential number or a random number to
the base filename for each item to be renamed. When the
user OKs the dialog the function calls the RenameItem
function for each item in the vector, passing it the
current and new filenames with the paths stripped off
(filenames without path.)
5. The RenameItem function uses the IShellFolder's
ParseDisplayName to get the relative pidl for the item to
be renamed. It then calls the IShellView's SetNameOf
function to rename the item with the new filename.
SetNameOf returns the new pidl for the renamed item. Then
the IShellView's SelectItem function is called with the
new pidl of the item.

All of this works. The item is renamed correctly and
SelectItem reports no errors, but as soon as the context
menu is finished the selection states are lost. Well, most
of them are lost anyway. If I have a bunch of files
selected, say 20-30, then ten files always remain selected
and the others become unselected.

Thanks for the help you've given me already. Any new
suggestions will be much appreciated.

Regards,
Mick
Jim Barry
2004-01-10 18:44:42 UTC
Permalink
3. My menu's InvokeCommand function gets called.
InvokeCommand calls SHGetDesktopFolder and uses that
interface's ParseDisplayName to get the full pidl of the
folder saved in step 1. The folder pidl is then passed to
the desktop's IShellFolder::BindToObject function which
returns the IShellFolder for the folder. This
IShellFolder's CreateViewObject function is called to get
the IShellView for the folder.
That won't retrieve the IShellView for the view object that created your handler. Instead, it will create a completely new view object. To get an interface for the existing view object you would have to go via the site object or WM_GETISHELLBROWSER as discussed previously.
5. The RenameItem function uses the IShellFolder's
ParseDisplayName to get the relative pidl for the item to
be renamed. It then calls the IShellView's SetNameOf
function to rename the item with the new filename.
SetNameOf is a method of IShellFolder.
All of this works. The item is renamed correctly and
SelectItem reports no errors, but as soon as the context
menu is finished the selection states are lost. Well, most
of them are lost anyway. If I have a bunch of files
selected, say 20-30, then ten files always remain selected
and the others become unselected.
This is to do with the shell's change notification mechanism. When you rename the files via SetNameOf, SHCNE_RENAMEITEM notifications are posted. These notifications are asynchronous and are being delivered after you select the items, causing the selection to be lost.

You need to remember the new ID-lists of the renamed items (as returned in the ppidlOut parameter of SetNameOf) and flush the pending shell notifications before selecting the items.

Shell notifications are flushed using SHChangeNotify with the SHCNF_FLUSH flag. Unfortunately you can't use that flag on its own, but if you send a flushed SHCNE_UPDATEDIR notification to the folder in question, that might well do the trick.

- Jim
Michael Newton
2004-01-11 01:18:40 UTC
Permalink
Post by Jim Barry
Post by Mick
IShellFolder's CreateViewObject function is called to get
the IShellView for the folder.
That won't retrieve the IShellView for the view object that created your
handler. Instead, it will create a completely new view object. To get an
interface for the existing view object you would have to go via the site
object or WM GETISHELLBROWSER as discussed previously.
So, if I get an IShellBrowser interface by calling SendMessage with
the handle passed into my context menu's InvokeCommand, which is
supposed to be the handle of the window that is the owner of the
shortcut menu, then call its QueryActiveShellView, would that get me
the right IShellView?
Post by Jim Barry
This is to do with the shell's change notification mechanism. When you
rename the files via SetNameOf, SHCNE RENAMEITEM notifications are
posted. These notifications are asynchronous and are being delivered
after you select the items, causing the selection to be lost.
You need to remember the new ID-lists of the renamed items (as returned
in the ppidlOut parameter of SetNameOf) and flush the pending shell
notifications before selecting the items.
Shell notifications are flushed using SHChangeNotify with the
SHCNF FLUSH flag. Unfortunately you can't use that flag on its own, but
if you send a flushed SHCNE UPDATEDIR notification to the folder in
question, that might well do the trick.
I hadn't even thought of the shell posting asynchronous update
notifications. Okay, I'll try flushing the pending notifications and
let you know how it turns out.

Thanks Jim!

--Mick
Jim Barry
2004-01-11 15:09:19 UTC
Permalink
Post by Michael Newton
So, if I get an IShellBrowser interface by calling SendMessage with
the handle passed into my context menu's InvokeCommand, which is
supposed to be the handle of the window that is the owner of the
shortcut menu, then call its QueryActiveShellView, would that get me
the right IShellView?
Yes, that's the idea.

- Jim
Michael Newton
2004-01-11 18:31:38 UTC
Permalink
"Jim Barry" <***@mvps.org> wrote in message news:<***@TK2MSFTNGP12.phx.gbl>...

Jim,

Okay, it's all working fine now. Thank you very much for your help.

Of course now I have other questions... <g>

Regards,
Mick

Mick
2004-01-10 16:22:05 UTC
Permalink
-----Original Message-----
Hmmm, not really... it seems to work pretty well for me.
Maybe you should post some code so we can see what you're
doing. Where is your handler registered?

Jim,

Are you saying that you're using IShellView::SelectItem
and it's actually working? As I said before, I am and it's
not working. I call SelectItem and pass it a folder
relative pidl and I get no error but the item is not
selected.

I could post the code since it's just a utility I wrote
for my own use, but it's pretty big and I'd need to pare
it down before I could post it. Please, do me a favor and
give me a sanity check with what I'm currently doing.

1. My context menu's Initialize function gets called.
The function uses the passed-in IdataObject to build a
vector of strings containing the full paths of the
selected items. The function also extracts the folder's
path using the path of the first selected item. The vector
of file paths and the folder path are saved as data
members in the class.
2. The user selects a menu command. In this
case "Rename All".
3. My menu's InvokeCommand function gets called.
InvokeCommand calls SHGetDesktopFolder and uses that
interface's ParseDisplayName to get the full pidl of the
folder saved in step 1. The folder pidl is then passed to
the desktop's IShellFolder::BindToObject function which
returns the IShellFolder for the folder. This
IShellFolder's CreateViewObject function is called to get
the IShellView for the folder. InvokeCommand then calls
the RenameAll member function of the context menu class.
4. The RenameAll function pops up a dialog which
allows the user to enter a base filename and set an option
to append either a sequential number or a random number to
the base filename for each item to be renamed. When the
user OKs the dialog the function calls the RenameItem
function for each item in the vector, passing it the
current and new filenames with the paths stripped off
(filenames without path.)
5. The RenameItem function uses the IShellFolder's
ParseDisplayName to get the relative pidl for the item to
be renamed. It then calls the IShellView's SetNameOf
function to rename the item with the new filename.
SetNameOf returns the new pidl for the renamed item. Then
the IShellView's SelectItem function is called with the
new pidl of the item.

All of this works. The item is renamed correctly and
SelectItem reports no errors, but as soon as the context
menu is finished the selection states are lost. Well, most
of them are lost anyway. If I have a bunch of files
selected, say 20-30, then ten files always remain selected
and the others become unselected.

Thanks for the help you've given me already. Any new
suggestions will be much appreciated.

Regards,
Mick
Continue reading on narkive:
Loading...