BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Getting menus from Explorer plugin (TortoiseCVS)

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (ActiveX)
View previous topic :: View next topic  
Author Message
Pete Fraser
Guest





PostPosted: Wed Feb 01, 2006 4:03 pm    Post subject: Getting menus from Explorer plugin (TortoiseCVS) Reply with quote



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





PostPosted: Wed Feb 01, 2006 11:55 pm    Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) Reply with quote



"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





PostPosted: Wed Feb 01, 2006 11:59 pm    Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) Reply with 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

"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





PostPosted: Thu Feb 02, 2006 3:30 pm    Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) Reply with quote

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





PostPosted: Fri Feb 03, 2006 12:14 am    Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) Reply with quote

"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





PostPosted: Fri Feb 03, 2006 4:30 pm    Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) Reply with 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.

"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





PostPosted: Fri Feb 03, 2006 7:22 pm    Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) Reply with quote

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





PostPosted: Fri Feb 03, 2006 11:30 pm    Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) Reply with quote

"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





PostPosted: Fri Feb 03, 2006 11:51 pm    Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) Reply with quote

I tried that and it didn't add anything to the menu Sad
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





PostPosted: Sat Feb 04, 2006 3:25 am    Post subject: Re: Getting menus from Explorer plugin (TortoiseCVS) Reply with quote

"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 Sad

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
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (ActiveX) All times are GMT
Page 1 of 1

 
Jump to:  
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


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.