TIA
--
Stephen Brooker
ste...@yahoo.com
"Windows has detected that you have moved your mouse. You must restart for
this change to take effect."
> 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
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.
Gambit
Stephen Brooker <ste...@pacific.net.au> wrote in message
news:90s5vq$s4...@bornews.inprise.com...
.a
-- 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
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