 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Pete Fraser Guest
|
Posted: Wed Feb 01, 2006 4:03 pm Post subject: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
I am writing an explorer look-alike and need to be able to get
the context menus that explorer shows when no file is selected.
The basic menus are static so that's ok, but some are dynamic
- such as TortoiseCVS menus.
I've found the GUID from
HKEY_CLASSES_ROOT Directory\shellex\ContextMenuHandlers\TortoiseCVS
which is {5d1cb710-1c4b-11d4-bed5-005004b1f42f}
and I've been told to get the interface to it using:
_GUID GuiID =
Sysutils::StringToGUID("{5d1cb710-1c4b-11d4-bed5-005004b1f42f}");
CoCreateInstance(GuiID, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu,
(LPVOID*)&ContextMenu);
then I try to get it to fill in the menus using:
OleCheck(hResult = ContextMenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF, 0));
but hResult always returns 0 - no menus found.
What is the correct method to get the menus filled in?
My COM knowledge is non-existant and Web Sites don't seem
to help with this particular problem, so if anyone can help with this
it would really be appreciated - and might even help educate me!
I've also noticed that the GUID for ALL tortoise menus are identical
and don't understand how one GUID can support multiple interfaces.
Can someone explain this for me?
Thanks,
Pete |
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Wed Feb 01, 2006 11:55 pm Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
"Pete Fraser" <pete.nospam.fraser.nospam (AT) frasersoft (DOT) nospam.net> wrote in
message news:43e094bd (AT) newsgroups (DOT) borland.com...
| Quote: | CoCreateInstance(GuiID, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu,
(LPVOID*)&ContextMenu);
|
That is not the correct way to begin. You must initialize the shell
extension before you can use it. When you call CoCreateInstance(), retreive
the IShellExtInit interface rather than the IContextMenu interface. You can
then call the IShellExtInit::Initialize() method, passing it the ITEMIDLIST
of the desired folder. After that, you can then call QueryInterface() to
get the IContextMenu interface.
| Quote: | then I try to get it to fill in the menus using:
OleCheck(hResult = ContextMenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF, 0));
but hResult always returns 0
|
You did not intialize the shell extension first, so it does not know which
folder it is supposed to generate menu items for.
| Quote: | My COM knowledge is non-existant and Web Sites don't
seemto help with this particular problem
|
Did you read MSDN yet? All of this stuff is explained.
Creating Shell Extension Handlers
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_int/shell_int_extending/extensionhandlers/shell_ext.asp
Creating Context Menu Handlers
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_int/shell_int_extending/extensionhandlers/contextmenuhandlers.asp
IShellExtInit::Initialize Method
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/ifaces/ishellextinit/Initialize.asp
| Quote: | I've also noticed that the GUID for ALL tortoise menus are identical
and don't understand how one GUID can support multiple interfaces.
|
That is the power of not only COM, but of multiple inheritance in C++. That
is why the IUnknown interface has a QueryInterface() method - a single
object can support (usually derive from) multiple interfaces.
QueryInterface() allows an object to return a pointer to an inteface that it
supports. Being able to support multiple interfaces in a single object
simplifies the development of COM objects, and allows them to be hosted in a
single DLL/EXE.
Gambit |
|
| Back to top |
|
 |
Pete Fraser Guest
|
Posted: Wed Feb 01, 2006 11:59 pm Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
Thanks Remy,
As always you da man!
I'll take a look at these urls and get myself up to speed.
I always find MSDN hard to find what I want - wood and trees etc.
Rgds Pete
"Remy Lebeau (TeamB)" <no.spam (AT) no (DOT) spam.com> wrote in message
news:43e10391$1 (AT) newsgroups (DOT) borland.com...
| Quote: |
"Pete Fraser" <pete.nospam.fraser.nospam (AT) frasersoft (DOT) nospam.net> wrote in
message news:43e094bd (AT) newsgroups (DOT) borland.com...
CoCreateInstance(GuiID, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu,
(LPVOID*)&ContextMenu);
That is not the correct way to begin. You must initialize the shell
extension before you can use it. When you call CoCreateInstance(),
retreive
the IShellExtInit interface rather than the IContextMenu interface. You
can
then call the IShellExtInit::Initialize() method, passing it the
ITEMIDLIST
of the desired folder. After that, you can then call QueryInterface() to
get the IContextMenu interface.
then I try to get it to fill in the menus using:
OleCheck(hResult = ContextMenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF,
0));
but hResult always returns 0
You did not intialize the shell extension first, so it does not know which
folder it is supposed to generate menu items for.
My COM knowledge is non-existant and Web Sites don't
seemto help with this particular problem
Did you read MSDN yet? All of this stuff is explained.
Creating Shell Extension Handlers
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_int/shell_int_extending/extensionhandlers/shell_ext.asp
Creating Context Menu Handlers
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_int/shell_int_extending/extensionhandlers/contextmenuhandlers.asp
IShellExtInit::Initialize Method
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/ifaces/ishellextinit/Initialize.asp
I've also noticed that the GUID for ALL tortoise menus are identical
and don't understand how one GUID can support multiple interfaces.
That is the power of not only COM, but of multiple inheritance in C++.
That
is why the IUnknown interface has a QueryInterface() method - a single
object can support (usually derive from) multiple interfaces.
QueryInterface() allows an object to return a pointer to an inteface that
it
supports. Being able to support multiple interfaces in a single object
simplifies the development of COM objects, and allows them to be hosted in
a
single DLL/EXE.
Gambit
|
|
|
| Back to top |
|
 |
Pete Fraser Guest
|
Posted: Thu Feb 02, 2006 3:30 pm Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
Remy,
Thanks for your reply yesterday. MSDN explains for handlers but not so well
for hosts.
I now have:
CoCreateInstance(GuiID, NULL, CLSCTX_INPROC_SERVER, OleCheck(,
(LPVOID*)&ShellExtInit);
if (ShellExtInit)
{
IDataObject *DataObject=NULL;
OleCheck(ShellExtInit->Initialize(VirtualExplorerListviewEx->RootFolderNamespace->ParseDisplayName(),
DataObject, 0));
CoCreateInstance(GuiID, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu,
(LPVOID*)&ContextMenu);
if (ContextMenu)
{
HRESULT hResult;
OleCheck(hResult = ContextMenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF,
0));
// get this far but hResult = 0
.....
but I'm now not sure how to create a DataObject: ( I really do not know OLE
at all)
so any help here is really appreciated.
Are there any methods to find other error codes? I would have assumed that
ShellExtInit->Initialize
would have returned an error if DataObject was null but nothing was obvious
Thanks, Pete
"Pete Fraser" <pete.fraser (AT) frasersoft (DOT) nospam.com> wrote in message
news:43e1044a (AT) newsgroups (DOT) borland.com...
| Quote: | Thanks Remy,
As always you da man!
I'll take a look at these urls and get myself up to speed.
I always find MSDN hard to find what I want - wood and trees etc.
Rgds Pete |
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Fri Feb 03, 2006 12:14 am Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
"Pete Fraser" <pete.nospam.fraser.nospam (AT) frasersoft (DOT) nospam.net> wrote in
message news:43e1de88$1 (AT) newsgroups (DOT) borland.com...
| Quote: | CoCreateInstance(GuiID, NULL, CLSCTX_INPROC_SERVER, OleCheck(,
(LPVOID*)&ShellExtInit);
|
That should not even compile. It should be this instead:
CoCreateInstance(GuidID, NULL, CLSCTX_INPROC_SERVER, IID_IShellExtInit,
(LPVOID*)&ShellExtInit);
| Quote: | IDataObject *DataObject=NULL;
|
Since you are wanting to show a menu for a folder, you do not use a
IDataObject at all. That is only used when you want to retreive the menu
for 1 or more files, not folders.
| Quote: | CoCreateInstance(GuiID, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu,
(LPVOID*)&ContextMenu);
|
That is wrong. As I mentioned earlier, you should be using the
QueryInterface() method instead:
ShellExtInit->QueryInterface(IID_IContextMenu, (LPVOID*)&ContextMenu));
| Quote: | I'm now not sure how to create a DataObject
|
You have to derive your own class from IDataObject and then implement all of
the interface's methods:
IDataObject
http://msdn.microsoft.com/library/en-us/com/html/8a002deb-2727-456c-8078-a9b0d5893ed4.asp
For instance, allocate a memory block and fill it with the desired
filenames, in CF_HDROP format:
Shell Clipboard Formats
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_basics/shell_basics_programming/transferring/clipboard.asp
Then implement the IDataObject::GetData() method in your class to return the
contents of that memory block.
| Quote: | I would have assumed that ShellExtInit->Initialize would have
returned an error if DataObject was null
|
NULL is an allowed value. It specifies that no files are being requested,
only the folder itself.
Gambit |
|
| Back to top |
|
 |
Pete Fraser Guest
|
Posted: Fri Feb 03, 2006 4:30 pm Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
Thanks, I can now see the menu data in correct form.
Is there a way of merging a menu created by:
ContextMenu->QueryContextMenu(.....)
with the current VCL pop-up menu?
I tried:
OleCheck(hResult = ContextMenu->QueryContextMenu(hMenu, 0, 0, 0x7FFF,
CMF_NORMAL));
where hMenu was the Handle property of the Popup menu but nothing showed.
(Why would this be?)
Do I have to create the menu manually based on the menu created by
QueryContextMenu?
(Which does seem a shame but not an impossible task)
Thanks again for your help.
"Remy Lebeau (TeamB)" <no.spam (AT) no (DOT) spam.com> wrote in message
news:43e25982$1 (AT) newsgroups (DOT) borland.com...
| Quote: |
"Pete Fraser" <pete.nospam.fraser.nospam (AT) frasersoft (DOT) nospam.net> wrote in
message news:43e1de88$1 (AT) newsgroups (DOT) borland.com...
|
<snipped code> |
|
| Back to top |
|
 |
Pete Fraser Guest
|
Posted: Fri Feb 03, 2006 7:22 pm Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
I can get most aspects of the context menu onto my context menu....
except bitmaps. Obviously they can't get across as they are not in
the image list. Is there any way of doing this or would I be better
creating a standard Windows Popup menu and attaching that to
my form?
Is there a way of merging VCL and Windows popupmenus and
getting a sensible result with everything correctly displayed.
Rgds Pete
"Pete Fraser" <pete.nospam.fraser.nospam (AT) frasersoft (DOT) nospam.net> wrote in
message news:43e33e09 (AT) newsgroups (DOT) borland.com...
| Quote: | Thanks, I can now see the menu data in correct form.
Is there a way of merging a menu created by:
ContextMenu->QueryContextMenu(.....)
with the current VCL pop-up menu?
I tried:
OleCheck(hResult = ContextMenu->QueryContextMenu(hMenu, 0, 0, 0x7FFF,
CMF_NORMAL));
where hMenu was the Handle property of the Popup menu but nothing showed.
(Why would this be?)
Do I have to create the menu manually based on the menu created by
QueryContextMenu?
(Which does seem a shame but not an impossible task)
Thanks again for your help. |
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Fri Feb 03, 2006 11:30 pm Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
"Pete Fraser" <pete.nospam.fraser.nospam (AT) frasersoft (DOT) nospam.net> wrote in
message news:43e33e09 (AT) newsgroups (DOT) borland.com...
| Quote: | Is there a way of merging a menu created by:
ContextMenu->QueryContextMenu(.....)
with the current VCL pop-up menu?
|
Simply pass the TPopupMenu's Handle, or the Handle of a TMenuItem sub-menu,
as the hMenu parameter to QueryContextMenu(). When the user actually clicks
on one of the Explorer items, the VCL will not know how to handle it,
though, since the items are not TMenuItem objects. So you will have to
intercept the WM_COMMAND message and filter out the Explorer items manually.
That is why QueryContextMenu() lets you specify the starting and ending IDs
for the Explorer items to use - so that you can pick IDs that you know will
not conflict with any other menu items you may already have, and so you know
which IDs to look for when clicked.
Gambit |
|
| Back to top |
|
 |
Pete Fraser Guest
|
Posted: Fri Feb 03, 2006 11:51 pm Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
I tried that and it didn't add anything to the menu
Was that because the WM_COMMAND was not being handled?
We're getting close....
Thanks, Pete
"Remy Lebeau (TeamB)" <no.spam (AT) no (DOT) spam.com> wrote in message
news:43e3a0e4$1 (AT) newsgroups (DOT) borland.com...
| Quote: |
"Pete Fraser" <pete.nospam.fraser.nospam (AT) frasersoft (DOT) nospam.net> wrote in
message news:43e33e09 (AT) newsgroups (DOT) borland.com...
Is there a way of merging a menu created by:
ContextMenu->QueryContextMenu(.....)
with the current VCL pop-up menu?
Simply pass the TPopupMenu's Handle, or the Handle of a TMenuItem
sub-menu,
as the hMenu parameter to QueryContextMenu(). When the user actually
clicks
on one of the Explorer items, the VCL will not know how to handle it,
though, since the items are not TMenuItem objects. So you will have to
intercept the WM_COMMAND message and filter out the Explorer items
manually.
That is why QueryContextMenu() lets you specify the starting and ending
IDs
for the Explorer items to use - so that you can pick IDs that you know
will
not conflict with any other menu items you may already have, and so you
know
which IDs to look for when clicked. |
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Sat Feb 04, 2006 3:25 am Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) |
|
|
"Pete Fraser" <pete.fraser (AT) frasersoft (DOT) nospam.com> wrote in message
news:43e3a567$1 (AT) newsgroups (DOT) borland.com...
| Quote: | I tried that and it didn't add anything to the menu
|
Works fine when I use it.
| Quote: | Was that because the WM_COMMAND was not being handled?
|
No. The WM_COMMAND message only applies when a menu item is actually
clicked on. The message contains the ID of the item that was clicked. You
can pass that ID to IContextMenu::InvokeCommand() to execute the item's
action.
Incidently, during all of this discussion, you have only been loading the
menu for a specific plugin, not for a folder (which was your original goal).
To get the entire menu for a folder, you do not use CoCreateInstance() at
all. Use the IShellFolder interface instead.
You will have to first convert the desired folder path into an ITEMIDLIST
with the IShellFolder::ParseDisplayName() method, and then split the
ITEMIDLIST into its parent/child portions. Pass the parent portion to the
IShellFolder::BindToObject() method to get another IShellFolder interface
for that parent folder, and then pass the child portion to that
IShellFolder's GetUIObjectOf() method, which will return the IContextMenu
interface for the original path.
By using this technique, you can retreive any UI interface for not only
folders but for files as well, all using the same code. For example
(untested):
--- ItemIDList.h ---
#include <shlobj.h>
// These are helper classes to hide the difference between OS versions.
// The IL functions are only available on Win2K+. For earlier versions,
// ITEMIDLIST instances have to be parsed manually.
class TILFuncs
{
static LPITEMIDLIST WINAPI Internal_Clone(LPCITEMIDLIST pidl)
{
UINT cb = GetSize(pidl);
LPITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl;
LPITEMIDLIST pidlRet = (LPITEMIDLIST) ::CoTaskMemAlloc(cb);
if( pidlRet )
::CopyMemory(pidlRet, pidl, cb);
return pidlRet;
}
static LPITEMIDLIST WINAPI Internal_FindLast(LPCITEMIDLIST pidl)
{
if( (pidl) && (pidl->mkid.cb) )
{
LPITEMIDLIST pidlNext = (LPITEMIDLIST) pidl;
do
{
pidl = pidlNext;
pidlNext = GetNext(pidl);
}
while( pidlNext );
return pidl;
}
return NULL;
}
static void WINAPI Internal_Free(LPITEMIDLIST pidl)
{
if( pidl )
::CoTaskMemFree(pidl);
}
static LPITEMIDLIST WINAPI Internal_GetNext(LPCITEMIDLIST pidl)
{
LPITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl;
if( (pidlTemp) && (pidlTemp->mkid.cb) )
{
pidlTemp = (LPITEMIDLIST) (((LPBYTE) pidlTemp) +
pidlTemp->mkid.cb);
if( pidlTemp->mkid.cb )
return pidlTemp;
}
return NULL;
}
static UINT WINAPI Internal_GetSize(LPCITEMIDLIST pidl)
{
UINT cbTotal = 0;
if( pidl )
{
cbTotal += sizeof(pidl->mkid.cb);
do
{
cbTotal += pidl->mkid.cb;
pidl = GetNext(pidl);
}
while( pidl );
}
return cbTotal;
}
static BOOL WINAPI Internal_RemoveLast(LPITEMIDLIST pidl)
{
LPITEMIDLIST pidlLast = FindLast(pidl);
if( pidlLast )
{
pidlLast->mkid.cb = 0;
return TRUE;
}
return FALSE;
}
static void LoadFunc(LPCTSTR Name, LPVOID *Ptr, LPVOID Default)
{
*Ptr = ::GetProcAddress(hMod, Name);
if( *Ptr == NULL ) *Ptr = Default;
}
public:
TILFuncs()
{
HMODULE hShell32 = ::GetModuleHandle(TEXT("SHELL32"));
LoadFunc(hShell32, TEXT("ILCloneID"), &Clone, &Internal_Clone);
LoadFunc(hShell32, TEXT("ILFindLastID"), &FindLast,
&Internal_FindLast);
LoadFunc(hShell32, TEXT("ILFree"), &Free, &Internal_Free);
LoadFunc(hShell32, TEXT("ILGetNext"), &GetNext,
&Internal_GetNext);
LoadFunc(hShell32, TEXT("ILGetSize"), &GetSize,
&Internal_GetSize);
}
LPITEMIDLIST WINAPI (*Clone)(LPCITEMIDLIST);
LPITEMIDLIST WINAPI (*FindLast)(LPCITEMIDLIST);
void WINAPI (*Free)(LPITEMIDLIST);
LPITEMIDLIST WINAPI (*GetNext)(LPCITEMIDLIST);
UINT WINAPI (*GetSize)(LPCITEMIDLIST);
BOOL WINAPI (*RemoveLast)(LPITEMIDLIST);
};
class TItemIDList
{
private:
static TILFuncs ILFuncs;
LPITEMIDLIST pidl;
public:
TItemIDList(LPITEMIDLIST src = NULL) : pidl(src) {}
~TItemIDList() { ILFuncs.Free(pidl); }
bool Clone(LPITEMIDLIST *ppidl)
{
if( ppidl )
{
*ppidl = ILFuncs.Clone(pidl);
return (*ppidl);
}
return false;
}
bool RemoveLast(LPITEMIDLIST **ppidl = NULL)
{
if( !ppidl )
return ILFuncs.RemoveLast(pidl);
*ppidl = NULL;
LPITEMIDLIST pidlLast = ILFuncs.FindLast(pidl);
if( pidlLast )
{
*ppidl = ILFuncs.Clone(pidlLast);
if( *ppidl )
{
pidlLast->mkid.cb = 0;
return true;
}
}
return false;
}
LPITEMIDLIST operator&() { return &pidl; }
operator LPITEMIDLIST() { return pidl; }
}
--- Unit1.cpp ---
#include <utilcls.h>
#include "ItemIDList.h"
bool __fastcall GetShellUIObject(HWND hWnd, const WideString &wPath,
REFIID riid, LPVOID **pObj)
{
if( !pObj )
return false;
*pObj = NULL;
TComInterface<IShellFolder> Desktop;
::SHGetDesktopFolder(&Desktop);
if( Desktop )
{
TItemIDList pidlParent;
ULONG eaten = 0;
Desktop->ParseDisplayName(hWnd, NULL, wPath, &eaten,
&pidlParent, NULL);
if( pidlParent )
{
TItemIDList pidlChild;
if( pidlParent.RemoveLast(&pidlChild) )
{
TComInterface<IShellFolder> Folder;
Desktop->BindToObject(pidlParent, NULL,
IID_IShellFolder, (LPVOID*)&Folder);
if( Folder )
Folder->GetUIObjectOf(hWnd, 1, &pidlChild, riid,
NULL, pObj);
}
}
}
return (*pObj);
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TComInterface<IContextMenu> ContextMenu;
if( GetShellUIObject(Handle, "c:\\some folder\\", IID_IContextMenu,
(LPVOID*)&ContextMenu) )
// use ContextMenu as needed ...
}
Gambit |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|