Board index » cppbuilder » Shell popup menu

Shell popup menu


2005-06-21 09:54:52 AM
cppbuilder97
Is there a way to find out the items that windows explorer would use
displaying the popup menu for a specific file or folder?
 
 

Re:Shell popup menu

"Antreas7" < XXXX@XXXXX.COM >wrote in message
Quote
Is there a way to find out the items that windows explorer
would use displaying the popup menu for a specific file
or folder?
The only way to do that is to actually create a blank menu, pass it to
Explorer to fill in, and then see what was actually put into it. You use
the IContextMenu interface for that. You have to first retreive the
IShellFolder interface for the folder/file's parent folder. You can use
SHGetDesktopFolder(), IShellFolder::ParseDisplayName(), and
IShellFolder::BindToObject() for that. Once you have the parent folder's
IShellFolder interface, you can then call IShellFolder::GetUIObjectOf() to
get the IContentMenu interface, and then finally call
IContentMenu::QueryContextMenu().
Gambit
 

Re:Shell popup menu

Remy Lebeau (TeamB) wrote:
Quote
The only way to do that is to actually create a blank menu, pass it to
Explorer to fill in, and then see what was actually put into it. You use
the IContextMenu interface for that. You have to first retreive the
IShellFolder interface for the folder/file's parent folder. You can use
SHGetDesktopFolder(), IShellFolder::ParseDisplayName(), and
IShellFolder::BindToObject() for that. Once you have the parent folder's
IShellFolder interface, you can then call IShellFolder::GetUIObjectOf() to
get the IContentMenu interface, and then finally call
IContentMenu::QueryContextMenu().


Gambit
i am trying to do what you replied here and the problem that i have is
the following. I cannot seem to find what it is i have to pass where the
REFIID riid parameter is required in two of the above functions.
could you post a reply of some code? let's say for the folder
"C:\\windows"?
 

{smallsort}

Re:Shell popup menu

To be more specific i built the following test project...
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(OpenDialog1->Execute())
Label1->Caption=OpenDialog1->FileName;
else
Label1->Caption="";
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
PopupMenu->Items->Clear();
if(Label1->Caption.IsEmpty())
return;
unsigned long eaten=Label1->Caption.WideCharBufSize();
wchar_t *buf=new wchar_t[eaten];
Label1->Caption.WideChar(buf,eaten);
IShellFolder *shl;
ITEMIDLIST *item;
if(SHGetDesktopFolder(&shl)!=NOERROR)
{
ShowMessage("Desktop error");
return;
}
if(shl->ParseDisplayName(NULL,NULL,buf,&eaten,&item,NULL)!=NOERROR)
{
ShowMessage("Parse error");
return;
}
if(item==NULL)
{
ShowMessage("item failure");
return;
}
void *intface;
if(shl->BindToObject(item,NULL,IID_IContextMenu,&intface)!=NOERROR)
{
ShowMessage("Bind error");
return;
}
shl=(IShellFolder*)intface;
if(shl->GetUIObjectOf(NULL,1,(const ITEMIDLIST
**)&item,IID_IContextMenu,0,&intface)!=NOERROR)
{
ShowMessage("GetUI error");
return;
}
IContextMenu *menu;
menu=(IContextMenu*)intface;
eaten=menu->QueryContextMenu(PopupMenu->Handle,0,1,50,0);
Label1->Caption=eaten;
PopupMenu->Popup(10,10);
}
there is always a bind error. Can you show me what i should do to see
the popup menu??? what am i doing wrong?
 

Re:Shell popup menu

"qyte" < XXXX@XXXXX.COM >wrote in message
Quote
I cannot seem to find what it is i have to pass where the REFIID
riid parameter is required in two of the above functions.
You use IID_IShellFolder and IID_IContextMenu.
Quote
could you post a reply of some code?
LPITEMIDLIST GetNextItemID(LPCITEMIDLIST pidl)
{
if( pidl == NULL )
return NULL;
int cb = pidl->mkid.cb;
if( cb == 0 )
return NULL;
pidl = (LPITEMIDLIST) (((LPBYTE) pidl) + cb);
return (pidl->mkid.cb == 0) ? NULL : (LPITEMIDLIST) pidl;
}
UINT GetSize(LPCITEMIDLIST pidl)
{
UINT cbTotal = 0;
if( pidl != NULL )
{
cbTotal += sizeof(pidl->mkid.cb);
while( pidl != NULL )
{
cbTotal += pidl->mkid.cb;
pidl = GetNextItemID(pidl);
}
}
return cbTotal;
}
LPITEMIDLIST MakeCopy(LPCITEMIDLIST pidl)
{
LPITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl;
UINT cb = GetSize(pidl);
LPITEMIDLIST pidlRet = (LPITEMIDLIST) CoTaskMemAlloc(cb);
if( pidlRet != NULL )
CopyMemory(pidlRet, pidl, cb);
return pidlRet;
}
LPITEMIDLIST GetChildID(LPITEMIDLIST pidl)
{
LPITEMIDLIST pidlRet = NULL;
if( pidl != NULL )
{
if( pidl->mkid.cb )
{
LPITEMIDLIST pidlNext = pidl;
while( pidlNext )
{
pidl = pidlNext;
pidlNext = GetNextItemID(pidl);
}
pidlRet = MakeCopy(pidl);
if( pidlRet == NULL )
return NULL;
pidl->mkid.cb = 0;
}
return pidlRet;
}
{
IShellFolder *Desktop = NULL;
if( SUCCEEDED(SHGetDesktopFolder(&Desktop)) )
{
LPITEMIDLIST pidl = NULL;
ULONG ulEaten = 0;
WideString Path = Label1->Caption;
if( SUCCEEDED(Desktop->ParseDisplayName(NULL, NULL, Path,
&ulEaten, &pidl, NULL)) )
{
LPITEMIDLIST pidlChild = GetChildID(pidl);
if( pidlChild != NULL )
{
IShellFolder *ParentFolder = NULL;
if( SUCCEEDED(Desktop->BindToObject(pidl, NULL,
IID_IShellFolder, (LPVOID*)&ParentFolder)) )
{
IContextMenu *menu = NULL;
if( SUCCEEDED(ParentFolder->GetUIObjectOf(NULL, 1,
(LPCITEMIDLIST*)&pidlChild, IID_IContextMenu, NULL, (LPVOID*)&menu)) )
{
Label1->Caption =
menu->QueryContextMenu(PopupMenu->Handle, 0, 1, 100, CMF_NORMAL);
menu->Release();
PopupMenu->Popup(10, 10);
}
}
ParentFolder->Release();
}
}
CoTaskFreeMem(pidl);
}
Desktop->Release();
}
Gambit
 

Re:Shell popup menu

"qyte" < XXXX@XXXXX.COM >wrote in message
Quote
To be more specific i built the following test project...
That code is wrong for so many reasons. Asside from all the memory leaks
(you must call Release() on every interface you retrieve, and free the
ITEMIDLIST that ParseDisplayName() returns), you are also not using
BindToObject() correctly. For one thing, you requested it to return
IContextMenu and then are casting the returned pointer to IShellFolder
instead. That is completely wrong. Also, you did not heed what I told you
earlier:
"You have to first retreive the IShellFolder interface for the
folder/file's ***parent*** folder."
That is a very important step that you are completely ignoring. Please see
my other reply.
Gambit
 

Re:Shell popup menu

i have made the following project based on your reply.
LPITEMIDLIST GetNextItemID(LPCITEMIDLIST pidl)
{
if( pidl == NULL )
return NULL;
int cb = pidl->mkid.cb;
if( cb == 0 )
return NULL;
pidl = (LPITEMIDLIST) (((LPBYTE) pidl) + cb);
return (pidl->mkid.cb == 0) ? NULL : (LPITEMIDLIST) pidl;
}
UINT GetSize(LPCITEMIDLIST pidl)
{
UINT cbTotal = 0;
if( pidl != NULL )
{
cbTotal += sizeof(pidl->mkid.cb);
while( pidl != NULL )
{
cbTotal += pidl->mkid.cb;
pidl = GetNextItemID(pidl);
}
}
return cbTotal;
}
LPITEMIDLIST MakeCopy(LPCITEMIDLIST pidl)
{
LPITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl;
UINT cb = GetSize(pidl);
LPITEMIDLIST pidlRet = (LPITEMIDLIST) CoTaskMemAlloc(cb);
if( pidlRet != NULL )
CopyMemory(pidlRet, pidl, cb);
return pidlRet;
}
LPITEMIDLIST GetChildID(LPITEMIDLIST pidl)
{
LPITEMIDLIST pidlRet = NULL;
if( pidl != NULL )
{
if( pidl->mkid.cb )
{
LPITEMIDLIST pidlNext = pidl;
while( pidlNext )
{
pidl = pidlNext;
pidlNext = GetNextItemID(pidl);
}
pidlRet = MakeCopy(pidl);
if( pidlRet == NULL )
return NULL;
pidl->mkid.cb = 0;
}
}
return pidlRet;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(OpenDialog1->Execute())
Label1->Caption=OpenDialog1->FileName;
else
Label1->Caption="";
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if(Label1->Caption.IsEmpty())
return;
IShellFolder *Desktop = NULL;
if( SUCCEEDED(SHGetDesktopFolder(&Desktop)) )
{
LPITEMIDLIST pidl = NULL;
ULONG ulEaten = 0;
WideString Path = Label1->Caption;
if( SUCCEEDED(Desktop->ParseDisplayName(NULL, NULL,
Path,&ulEaten, &pidl, NULL)) )
{
LPITEMIDLIST pidlChild = GetChildID(pidl);
if( pidlChild != NULL )
{
IShellFolder *ParentFolder = NULL;
if( SUCCEEDED(Desktop->BindToObject(pidl,
NULL,IID_IShellFolder, (LPVOID*)&ParentFolder)) )
{
IContextMenu *menu = NULL;
if( SUCCEEDED(ParentFolder->GetUIObjectOf(NULL,
1,(LPCITEMIDLIST*)&pidlChild, IID_IContextMenu, NULL, (LPVOID*)&menu)) )
{
Label1->Caption
=menu->QueryContextMenu(PopupMenu->Handle, 0, 1, 100, CMF_NORMAL);
menu->Release();
PopupMenu->Popup(10, 10);
}
}
ParentFolder->Release();
}
}
CoTaskMemFree(pidl);
}
Desktop->Release();
}
the problem is that the popupmenu doesn't get any item in it at all.
there must be something wrong inside your code. the Label1 after i run
button 2 takes the value 114. is it right???
 

Re:Shell popup menu

"qyte" < XXXX@XXXXX.COM >wrote in message
Quote
i have made the following project based on your reply.
You did not need to include all of the code I gave you in your reply.
Quote
the problem is that the popupmenu doesn't get any item in it at all.
Yes, it does.
Quote
the Label1 after i run button 2 takes the value 114.
When QueryContextMenu() succeeds, it returns the ID of the last item that
was added to the menu. Which is exactly what you are getting.
Your menu does not get displayed because you are using a TPopupMenu with no
TMenuItem objects in it. If you use the Win32 API CreatePopupMenu() and
TrackPopupMenu() functions, you will see that the code I gave does actually
work.
Gambit
 

Re:Shell popup menu

Remy Lebeau (TeamB) wrote:
Quote
"qyte" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...


>To be more specific i built the following test project...


That code is wrong for so many reasons. Asside from all the memory leaks
(you must call Release() on every interface you retrieve, and free the
ITEMIDLIST that ParseDisplayName() returns), you are also not using
BindToObject() correctly. For one thing, you requested it to return
IContextMenu and then are casting the returned pointer to IShellFolder
instead. That is completely wrong. Also, you did not heed what I told you
earlier:

"You have to first retreive the IShellFolder interface for the
folder/file's ***parent*** folder."

That is a very important step that you are completely ignoring. Please see
my other reply.


Gambit



I used these function as you did and the popup menu worked fine.The
problem is that nothing is done when you click with the mouse an item of
the popup menu. How can i add the events on these items?
 

Re:Shell popup menu

"Antreas7" < XXXX@XXXXX.COM >wrote in message
Quote
I used these function as you did and the popup menu worked fine.
The problem is that nothing is done when you click with the mouse
an item of the popup menu.
Yes, it does. You just haven't set up the rest of your code to do anything
about it.
Quote
How can i add the events on these items?
If you use CreatePopupMenu() and TrackPopupMenu(), then TrackPopupMenu()
takes as a parameter the HWND that will receive the WM_COMMAND message for
the menu items. You can then intercept that message, pull out the ID of the
menu that was clicked on, and then call the IContextMenu's InvokeCommand()
method to perform the action for the clicked menu item.
Gambit
 

Re:Shell popup menu

Remy Lebeau (TeamB) wrote:
Quote
"Antreas7" < XXXX@XXXXX.COM >wrote in message
news:42bb287b$ XXXX@XXXXX.COM ...


>I used these function as you did and the popup menu worked fine.
>The problem is that nothing is done when you click with the mouse
>an item of the popup menu.


Yes, it does. You just haven't set up the rest of your code to do anything
about it.


>How can i add the events on these items?


If you use CreatePopupMenu() and TrackPopupMenu(), then TrackPopupMenu()
takes as a parameter the HWND that will receive the WM_COMMAND message for
the menu items. You can then intercept that message, pull out the ID of the
menu that was clicked on, and then call the IContextMenu's InvokeCommand()
method to perform the action for the clicked menu item.


Gambit


i have tried a different implementation and it works well.
i have made the following function.
void ConstructMenu(HMENU Popup, TMenuItem *item)
{
for(int i=0;i<GetMenuItemCount(Popup);i++)
{
TMenuItem *additem=new TMenuItem(Form1);
char buf[200];
GetMenuString(Popup,i,buf,200,MF_BYPOSITION);
additem->Caption=buf;
if(additem->Caption=="")
additem->Caption="-";
additem->OnClick=Form1->ExClick;
/*MENUITEMINFO inf;
inf.cbSize=sizeof(MENUITEMINFO);
inf.fMask=MIIM_TYPE;
if(GetMenuItemInfo(Popup,i,true,&inf) && (inf.fType &
MFT_BITMAP))
additem->Bitmap->Handle=(void*)LOWORD(inf.dwTypeData);*/
item->Add(additem);
HMENU Sub=GetSubMenu(Popup,i);
if(Sub)
ConstructMenu(Sub,additem);
}
}
As you can see i add the found items to my own popup menu.
I have faced two problems. first i cannot find out how to retrieve
the icon displayed left of a popupmenu item. look at the code inside the
/**/ and tell me what is wrong with that? everything else works fine.
apart from that the other problem is located somewhere inside your code.
For some reason although the popupmenu gets the right items the subitems
are not found. for example in the Open with item the is only one subitem
"Open with" again. that happens in the send to as well. Another problem
is that sometimes the invoke command does not function. like when i
click on copy or cut. but it works great on open, delete, properties etc.
Apart from that can you tell me if the code will need a lot variations
in case i have multiselected items? what will i have to put as path in
such a case?