Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Wi n Explorer Context Menu...

28 views
Skip to first unread message

Stephen Brooker

unread,
Dec 6, 2000, 3:00:00 AM12/6/00
to
I'm not sure this is the right NG or that I want to know the answer (I have
a funny feeling it's going to be harder than I want it to be)., but here
goes.
Can anyone tell me how to add my own menu items (in particular a sub menu)
to the Windows Explorer conext menus (ie when you right click on a
directory, file etc...).
If this is the wrong NG, can someone please point me to somewhere more
appropriate.

TIA

--
Stephen Brooker
ste...@yahoo.com

"Windows has detected that you have moved your mouse. You must restart for
this change to take effect."

Remy Lebeau

unread,
Dec 6, 2000, 3:00:00 AM12/6/00
to

"Stephen Brooker" <ste...@pacific.net.au> wrote in message
news:90l2bv$t1...@bornews.inprise.com...

> Can anyone tell me how to add my own menu items (in particular a sub menu)
> to the Windows Explorer conext menus (ie when you right click on a
> directory, file etc...).

You're right, it's a lot more involved then you think.

The short answer is that you have to create your own Shell Extension.

The long answer is that you have to make your own DLL that the system will
call into when it's creating those context menus. Your DLL would need to
implement the IContextMenu and IShellExtInit interfaces, and also export
DLLRegisterServer() and DLLUnregisterServer() functions that
register/unregister your DLL with the registry in the
HKEY_CLASSES_ROOT\Folder\shellex\ContextMenuHandlers key, and a
DllGetClassObject() function that initializes your code and returns the
requested objects to the system for it to use.

> If this is the wrong NG, can someone please point me to somewhere more
> appropriate.

This is the right newsgroup


Gambit


Stephen Brooker

unread,
Dec 8, 2000, 9:39:06 PM12/8/00
to

--
Stephen Brooker
ste...@yahoo.com

"Windows has detected that you have moved your mouse. You must restart for
this change to take effect."

"> You're right, it's a lot more involved then you think.
>

Thats what I thought.
I'm feeling brave though, anyone know of any examples anywhere?

Thanks Gambit.


Remy Lebeau

unread,
Dec 11, 2000, 12:44:04 PM12/11/00
to
I can send you an example later tonight of what I use in one of my projects


Gambit

Stephen Brooker <ste...@pacific.net.au> wrote in message

news:90s5vq$s4...@bornews.inprise.com...

Alex Bakaev

unread,
Dec 11, 2000, 5:38:33 PM12/11/00
to
MSDN has pretty good examples. Also, I think I posted some pieces of
code in the .activex group (or this one)

.a

Remy Lebeau

unread,
Dec 11, 2000, 10:04:11 PM12/11/00
to
As promised, here is a sample framework of the code I use in my project:

-- DLL source ---

#include "ClsFact.h"
#include "DebugLog.h"
#include <shlobj.h>

USEUNIT("ClsFact.cpp");
USEUNIT("contexthandler.cpp");

HINSTANCE hInst;
extern ULONG g_DllRefCount;

// generated with CoCreateGuid()
const char *szCLSID = "{5637FAA5-A5A4-42DD-A9F2-AE08F204B5AB}";

__declspec(dllexport) HRESULT STDMETHODCALLTYPE DllCanUnloadNow(void)
{
return (g_DllRefCount > 0 ? S_FALSE : S_OK);
}

__declspec(dllexport) HRESULT STDMETHODCALLTYPE DllGetClassObject(REFCLSID
rclsid, REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

// create a CClassFactory object and check it for validity

CClassFactory *pClassFactory = new CClassFactory(rclsid);
if(pClassFactory == NULL)
return E_OUTOFMEMORY;

// get the QueryInterface return for our return value
HRESULT hResult = pClassFactory->QueryInterface(riid, ppReturn);

// call Release to decement the ref count - creating the object set it
to one
// and QueryInterface incremented it - since its being used externally
(not by
// us), we only want the ref count to be 1
pClassFactory->Release();

// return the result from QueryInterface
return hResult;
}

__declspec(dllexport) HRESULT STDMETHODCALLTYPE DllRegisterServer(void)
{
HKEY hKey;
DWORD unused;
HRESULT hRes = SELFREG_E_CLASS;

if(RegCreateKeyEx(HKEY_CLASSES_ROOT,
"CLSID",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&hKey,
&unused) == ERROR_SUCCESS)
{
if(RegCreateKey(hKey, szCLSID, &hKey) == ERROR_SUCCESS)
{
RegSetValueEx(hKey,
NULL,
0,
REG_SZ,
(BYTE*)"My Custom DLL",
15);

if(RegCreateKey(hKey, "InProcServer32", &hKey) == ERROR_SUCCESS)
{
char lpFilename[MAX_PATH] = {0};
GetModuleFileName(hInst,
lpFilename,
MAX_PATH);
RegSetValueEx(hKey,
NULL,
0,
REG_SZ,
(BYTE*)lpFilename,
strlen(lpFilename)+1);

RegSetValueEx(hKey,
"ThreadingModel",
0,
REG_SZ,
(BYTE*)"Apartment",
10);
}
}
RegCloseKey(hKey);

if(RegCreateKeyEx(HKEY_CLASSES_ROOT,
"Folder\\shellex\\ContextMenuHandlers\\My Custom
DLL",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&hKey,
&unused) == ERROR_SUCCESS)
{
RegSetValueEx(hKey,
NULL,
0,
REG_SZ,
(BYTE*)szCLSID,
strlen(szCLSID) + 1);

RegCloseKey(hKey);

hRes = S_OK;
}
}

SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
return hRes;
}

__declspec(dllexport) HRESULT STDMETHODCALLTYPE DllUnregisterServer(void)
{
HKEY hKey;
DWORD keys = 0, size = 0;
LONG res = ERROR_SUCCESS;
char *szCLSIDkey = new char[6+strlen(szCLSID)];
wsprintf(szCLSIDkey, "CLSID\\%s", szCLSID);

if(RegOpenKeyEx(HKEY_CLASSES_ROOT,
"Folder\\shellex\\ContextMenuHandlers",
0,
KEY_WRITE,
&hKey) == ERROR_SUCCESS)
{
RegQueryInfoKey(hKey,
NULL,
NULL,
NULL,
&keys,
&size,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL);

char *buf = new char[size+1];
for(DWORD x = 0; x < keys; x++)
{
RegEnumKey(hKey,
x,
buf,
size+1);

if(strcmpi(buf, "My Custom DLL") == 0)
{
res = RegDeleteKey(hKey, "My Custom DLL");
break;
}
}
delete[] buf;
RegCloseKey(hKey);

if(RegOpenKeyEx(HKEY_CLASSES_ROOT,
szCLSIDkey,
0,
KEY_WRITE,
&hKey) == ERROR_SUCCESS)
{
res = RegDeleteKey(hKey, "InProcServer32");
RegCloseKey(hKey);

if(RegOpenKeyEx(HKEY_CLASSES_ROOT,
"CLSID",
0,
KEY_WRITE,
&hKey) == ERROR_SUCCESS)

{
res = RegDeleteKey(hKey, szCLSID);
RegCloseKey(hKey);
}
}
}
delete[] szCLSIDkey;

SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);

if(res == ERROR_SUCCESS)
return S_OK;
else
return SELFREG_E_CLASS;
}


int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
switch(reason)
{
case DLL_PROCESS_ATTACH:
hInst = hinst;
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

--- ClsFact ---

/**************************************************************************
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Copyright 1997 Microsoft Corporation. All Rights Reserved.
**************************************************************************/

/**************************************************************************

File: ClsFact.h

Description: CClassFactory definitions.

**************************************************************************/

#ifndef CLASSFACTORY_H
#define CLASSFACTORY_H

#include <windows.h>
#include <shlobj.h>

/**************************************************************************

CClassFactory class definition

**************************************************************************/

class CClassFactory : public IClassFactory
{
protected:
DWORD m_ObjRefCount;

public:
CClassFactory(CLSID);
~CClassFactory();

// IUnknown methods
STDMETHODIMP QueryInterface(REFIID, LPVOID*);
STDMETHODIMP_(DWORD) AddRef();
STDMETHODIMP_(DWORD) Release();

// IClassFactory methods
STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID*);
STDMETHODIMP LockServer(BOOL);

private:
CLSID m_clsidObject;
};


#endif // CLASSFACTORY_H


/**************************************************************************

File: ClsFact.cpp

Description: Implements CClassFactory.

**************************************************************************/

/**************************************************************************
#include statements
**************************************************************************/

#include "ClsFact.h"
#include "contexthandler.h"

/**************************************************************************
private function prototypes
**************************************************************************/

/**************************************************************************
global variables
**************************************************************************/

ULONG g_DllRefCount = 0;

///////////////////////////////////////////////////////////////////////////
//
// IClassFactory implementation
//

/**************************************************************************

CClassFactory::CClassFactory

**************************************************************************/

CClassFactory::CClassFactory(CLSID clsid)
{
m_clsidObject = clsid;
m_ObjRefCount = 1;
g_DllRefCount++;
}

/**************************************************************************

CClassFactory::~CClassFactory

**************************************************************************/

CClassFactory::~CClassFactory()
{
g_DllRefCount--;
}

/**************************************************************************

CClassFactory::QueryInterface

**************************************************************************/

STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

if(IsEqualIID(riid, IID_IUnknown))
*ppReturn = this;
else if(IsEqualIID(riid, IID_IClassFactory))
*ppReturn = (IClassFactory*)this;

if(*ppReturn)
{
(*(LPUNKNOWN*)ppReturn)->AddRef();
return S_OK;
}

return E_NOINTERFACE;
}

/**************************************************************************

CClassFactory::AddRef

**************************************************************************/

STDMETHODIMP_(DWORD) CClassFactory::AddRef()
{
return ++m_ObjRefCount;
}

/**************************************************************************

CClassFactory::Release

**************************************************************************/

STDMETHODIMP_(DWORD) CClassFactory::Release()
{
if(--m_ObjRefCount == 0)
{
delete this;
return 0;
}

return m_ObjRefCount;
}

/**************************************************************************

CClassFactory::CreateInstance

**************************************************************************/

STDMETHODIMP CClassFactory::CreateInstance(LPUNKNOWN pUnknown, REFIID riid,
LPVOID *ppObject)
{
HRESULT hResult = E_FAIL;
LPVOID pTemp = NULL;
CContextMenuHandler *pContextMenu = NULL;

*ppObject = NULL;

if(pUnknown != NULL)
{
return CLASS_E_NOAGGREGATION;
}

// create the proper object
if(IsEqualIID(riid, IID_IContextMenu) ||
IsEqualIID(riid, IID_IShellExtInit))
{
pContextMenu = new CContextMenuHandler();
if(pContextMenu == NULL)
{
return E_OUTOFMEMORY;
}

if(IsEqualIID(riid, IID_IContextMenu))
pTemp = (IContextMenu*)pContextMenu;
else if(IsEqualIID(riid, IID_IShellExtInit))
pTemp = (IShellExtInit*)pContextMenu;
}

if(pTemp)
{
// get the QueryInterface return for our return value
hResult = ((LPUNKNOWN)pTemp)->QueryInterface(riid, ppObject);

// call Release to decement the ref count
((LPUNKNOWN)pTemp)->Release();
}

return hResult;
}

/**************************************************************************

CClassFactory::LockServer

**************************************************************************/

STDMETHODIMP CClassFactory::LockServer(BOOL)
{
return E_NOTIMPL;
}


--- contexthandler ---

#ifndef contexthandlerH
#define contexthandlerH

#include <shlobj.h>

class CContextMenuHandler : public IContextMenu, IShellExtInit
{
private:
DWORD cRefs;
char szFileName[MAX_PATH];
IDataObject *p_DataObj;
public:
CContextMenuHandler();
~CContextMenuHandler();

STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj);
STDMETHODIMP_(DWORD) AddRef(void);
STDMETHODIMP_(DWORD) Release(void);

STDMETHODIMP QueryContextMenu(HMENU, UINT, UINT, UINT, UINT);
STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO);
STDMETHODIMP GetCommandString(UINT, UINT, UINT*, LPSTR, UINT);

STDMETHODIMP Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY);
};
typedef CContextMenuHandler* LPCONTEXTMENUHANDLER;

#endif


#include "contexthandler.h"

extern ULONG g_DllRefCount;

CContextMenuHandler::CContextMenuHandler()
{
cRefs = 0;
g_DllRefCount++;
}

CContextMenuHandler::~CContextMenuHandler()
{
g_DllRefCount--;
}

STDMETHODIMP CContextMenuHandler::QueryInterface(REFIID riid, LPVOID *ppv)
{
*ppv = NULL;

if(IsEqualIID(riid, IID_IUnknown))
*ppv = this;
else if(IsEqualIID(riid, IID_IContextMenu))
*ppv = (IContextMenu*)this;
else if(IsEqualIID(riid, IID_IShellExtInit))
*ppv = (IShellExtInit*)this;

if (*ppv == NULL)
{
return E_NOINTERFACE;
}

((LPUNKNOWN)*ppv)->AddRef();

return NOERROR;
}

STDMETHODIMP_(DWORD) CContextMenuHandler::AddRef(void)
{
return ++cRefs;
}

STDMETHODIMP_(DWORD) CContextMenuHandler::Release(void)
{
if (--cRefs != 0)
{
return cRefs;
}
delete this;

return 0;
}

STDMETHODIMP CContextMenuHandler::QueryContextMenu(HMENU hMenu, UINT
indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
if(((uFlags & 0x000F) == CMF_NORMAL) || (uFlags & CMF_EXPLORE))
{
HMENU hSubMenu = CreatePopupMenu();
UINT idCmd = idCmdFirst;

InsertMenu(hSubMenu,
0xFFFFFFFF,
MF_STRING | MF_BYPOSITION,
idCmd++,
"Item 1");

InsertMenu(hSubMenu,
0xFFFFFFFF,
MF_STRING | MF_BYPOSITION,
idCmd++,
"Item 2");

// add submenu to menu passed
InsertMenu(hMenu,
indexMenu++,
MF_SEPARATOR | MF_BYPOSITION,
0,
NULL);

InsertMenu(hMenu,
indexMenu++,
MF_STRING | MF_BYPOSITION | MF_POPUP,
(UINT)hSubMenu,
"&My Sub Menu");

return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(idCmd-idCmdFirst));
}

return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}

#define CMD_ADD 0
STDMETHODIMP CContextMenuHandler::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
{
BOOL fEx = FALSE;
BOOL fUnicode = FALSE;

if(lpici->cbSize = sizeof(CMINVOKECOMMANDINFOEX))
{
fEx = TRUE;
if(lpici->fMask & CMIC_MASK_UNICODE)
fUnicode = TRUE;
}

if(!fUnicode && HIWORD(lpici->lpVerb))
{
if(strcmpi(lpici->lpVerb, "listing"))
{
return E_FAIL;
}
}
else if(fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX*)lpici)->lpVerbW))
{
if(_wcsicmp(((CMINVOKECOMMANDINFOEX*)lpici)->lpVerbW, L"listing"))
{
return E_FAIL;
}
}
else
{
// code here for executing actions when menus are clicked
}

return S_OK;
}

STDMETHODIMP CContextMenuHandler::GetCommandString(UINT idCmd, UINT uType,
UINT *pwReserved, LPSTR pszName, UINT cchMax)
{
HRESULT hr = E_INVALIDARG;

switch(uType)
{
case GCS_HELPTEXTA:
lstrcpynA(pszName, "Display File Name", cchMax);
hr = S_OK;
break;

case GCS_HELPTEXTW:
lstrcpynW((LPWSTR)pszName, L"Display File Name",
cchMax);
hr = S_OK;
break;

case GCS_VERBA:
lstrcpynA(pszName, "listing", cchMax);
hr = S_OK;
break;

case GCS_VERBW:
lstrcpynW((LPWSTR)pszName, L"listing", cchMax);
hr = S_OK;
break;

default:
hr = S_OK;
break;
}

return hr;
}

STDMETHODIMP CContextMenuHandler::Initialize(LPCITEMIDLIST pIDFolder,
IDataObject *pData, HKEY hRegKey)
{
// If Initialize has already been called, release the old
// IDataObject pointer.
if(p_DataObj)
p_DataObj->Release();

// If a data object pointer was passed in, save it and
// extract the file name.
if(pData)
{
p_DataObj = pData;
pData->AddRef();

STGMEDIUM medium;
FORMATETC fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1,
TYMED_HGLOBAL};
UINT uCount;

if(SUCCEEDED(p_DataObj->GetData(&fe, &medium)))
{
// Get the file name from the CF_HDROP.
uCount = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL,
0);
if(uCount)
DragQueryFile((HDROP)medium.hGlobal, 0, szFileName,
sizeof(szFileName));

ReleaseStgMedium(&medium);
}
}

return S_OK;
}


Gambit

"Remy Lebeau" <gamb...@gte.net> wrote in message
news:9134ul$9a...@bornews.inprise.com...

Stephen Brooker

unread,
Dec 12, 2000, 5:01:20 AM12/12/00
to
Thanks Alex and once again Gambit. Appreciate the help.


--
Stephen Brooker
ste...@yahoo.com

"Windows has detected that you have moved your mouse. You must restart for
this change to take effect."

"Stephen Brooker" <ste...@pacific.net.au> wrote in message

news:90l2bv$t1...@bornews.inprise.com...


> I'm not sure this is the right NG or that I want to know the answer (I
have
> a funny feeling it's going to be harder than I want it to be)., but here
> goes.

> Can anyone tell me how to add my own menu items (in particular a sub menu)
> to the Windows Explorer conext menus (ie when you right click on a
> directory, file etc...).

> If this is the wrong NG, can someone please point me to somewhere more
> appropriate.
>

> TIA

0 new messages