I'm working on a custom context menu, and I was able to register it and it shows in the file explorer, but I want to put it next to another existed context menu.
But the position is not what I want. I want to put it another position:
Any ideas? Thanks.
Add the source code here:
It's a sample downloaded from: https://www.codeproject.com/Articles/583726/Writing-a-shell-extension-in-plain-Cplusplus
ShellExt.cpp
// ShellExt.cpp: implementation of the CShellExt class.
//
//////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <tchar.h>
#include <shlobj.h>
#include "ShellExt.h"
#include "Guid.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CShellExt::CShellExt()
{
}
CShellExt::~CShellExt()
{
}
STDMETHODIMP_(DWORD) CShellExt::Release(void)
{
if(--m_ObjRefCount == 0)
{
delete this;
return 0;
}
return m_ObjRefCount;
}
STDMETHODIMP_(DWORD) CShellExt::AddRef(void)
{
return ++m_ObjRefCount;
}
STDMETHODIMP CShellExt::QueryInterface(REFIID riid, LPVOID *ppReturn )
{
*ppReturn = NULL;
if( IsEqualIID(riid, IID_IUnknown) )
*ppReturn = this;
else if( IsEqualIID(riid, IID_IClassFactory) )
*ppReturn = (IClassFactory*)this;
else if ( IsEqualIID(riid, IID_IShellExtInit))
*ppReturn = (IShellExtInit*)this;
else if ( IsEqualIID(riid, IID_IContextMenu))
*ppReturn = (IContextMenu*)this;
if( *ppReturn )
{
LPUNKNOWN pUnk = (LPUNKNOWN)(*ppReturn);
pUnk->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP CShellExt::Initialize (
LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID )
{
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stg = { TYMED_HGLOBAL };
HDROP hDrop;
// Look for CF_HDROP data in the data object.
if ( FAILED( pDataObj->GetData ( &fmt, &stg ) ))
{
// Nope! Return an "invalid argument" error back to Explorer.
return E_INVALIDARG;
}
// Get a pointer to the actual data.
hDrop = (HDROP) GlobalLock ( stg.hGlobal );
// Make sure it worked.
if ( NULL == hDrop )
return E_INVALIDARG;
// Sanity check - make sure there is at least one filename.
UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
HRESULT hr = S_OK;
if ( 0 == uNumFiles )
{
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return E_INVALIDARG;
}
// Get the name of the first file and store it in our member variable m_szFile.
if ( 0 == DragQueryFile ( hDrop, 0, m_szFile, MAX_PATH ) )
hr = E_INVALIDARG;
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return hr;
}
STDMETHODIMP CShellExt::QueryContextMenu (
HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd,
UINT uidLastCmd, UINT uFlags )
{
// If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
if ( uFlags & CMF_DEFAULTONLY )
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );
InsertMenu ( hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, _T("HelloExtNoAtl Test Item") );
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 );
}
STDMETHODIMP CShellExt::GetCommandString (
UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax )
{
//USES_CONVERSION;
// Check idCmd, it must be 0 since we have only one menu item.
if ( 0 != idCmd )
return E_INVALIDARG;
// If Explorer is asking for a help string, copy our string into the
// supplied buffer.
if ( uFlags & GCS_HELPTEXT )
{
LPCTSTR szText = _T("This is the simple shell extension's help");
if ( uFlags & GCS_UNICODE )
{
DWORD len;
// Get the length (in wide chars) of the UNICODE buffer we'll need to
// convert MyString to UNICODE.
len = MultiByteToWideChar(CP_ACP, 0, szText, -1, 0, 0);
BSTR strptr;
strptr = SysAllocStringLen(0, len);
// Convert MyString to UNICODE in the buffer allocated by SysAllocStr
MultiByteToWideChar(CP_ACP, 0, szText, -1, strptr, len);
// We need to cast pszName to a Unicode string, and then use the
// Unicode string copy API.
lstrcpynW ( (LPWSTR) pszName, (LPWSTR)(strptr), cchMax );
SysFreeString(strptr);
}
else
{
// Use the ANSI string copy API to return the help string.
lstrcpynA ( pszName, (LPSTR)szText, cchMax );
}
return S_OK;
}
return E_INVALIDARG;
}
STDMETHODIMP CShellExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )
{
// If lpVerb really points to a string, ignore this function call and bail out.
if ( 0 != HIWORD( pCmdInfo->lpVerb ) )
return E_INVALIDARG;
// Get the command index - the only valid one is 0.
switch ( LOWORD( pCmdInfo->lpVerb) )
{
case 0:
{
TCHAR szMsg [MAX_PATH + 32];
wsprintf ( szMsg, _T("The selected file was:\n\n%s"), m_szFile );
MessageBox ( pCmdInfo->hwnd, szMsg, _T("HelloShlExt"),
MB_ICONINFORMATION );
return S_OK;
}
break;
default:
return E_INVALIDARG;
break;
}
}
STDAPI DllRegisterServer( VOID )
{
INT i;
HKEY hKey;
LRESULT lResult;
DWORD dwDisp;
TCHAR szSubKey[MAX_PATH];
TCHAR szCLSID[MAX_PATH];
TCHAR szModule[MAX_PATH];
LPWSTR pwsz;
// get the CLSID in string form
StringFromIID( CLSID_HelloExtNoAtl, &pwsz );
if( pwsz )
{
WideCharToLocal( szCLSID, pwsz, ARRAYSIZE(szCLSID) );
LPMALLOC pMalloc;
CoGetMalloc(1, &pMalloc);
if( pMalloc )
{
pMalloc->Free(pwsz);
pMalloc->Release();
}
}
// get this DLL's path and file name
GetModuleFileName( g_hInst, szModule, ARRAYSIZE(szModule) );
// CLSID entries
REGSTRUCT ClsidEntries[] = {
HKEY_CLASSES_ROOT, TEXT("CLSID\\%s"), NULL, TEXT("HelloExtNoAtl"),
HKEY_CLASSES_ROOT, TEXT("CLSID\\%s"), TEXT("InfoTip"), TEXT("HelloExtNoAtl."),
HKEY_CLASSES_ROOT, TEXT("CLSID\\%s\\InprocServer32"), NULL, TEXT("%s"),
HKEY_CLASSES_ROOT, TEXT("CLSID\\%s\\InprocServer32"), TEXT("ThreadingModel"), TEXT("Apartment"),
HKEY_CLASSES_ROOT, TEXT("CLSID\\%s\\DefaultIcon"), NULL, TEXT("%s,0"),
NULL, NULL, NULL, NULL};
for( i=0; ClsidEntries[i].hRootKey; i++ )
{
// create the sub key string.
wsprintf( szSubKey, ClsidEntries[i].lpszSubKey, szCLSID );
lResult = RegCreateKeyEx( ClsidEntries[i].hRootKey,
szSubKey,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&hKey,
&dwDisp );
if( lResult==NOERROR )
{
TCHAR szData[MAX_PATH];
wsprintf(szData, ClsidEntries[i].lpszData, szModule);
lResult = RegSetValueEx( hKey,
ClsidEntries[i].lpszValueName,
0,
REG_SZ,
(LPBYTE)szData,
lstrlen(szData) + 1);
RegCloseKey(hKey);
}
else
return SELFREG_E_CLASS;
}
// Context Menu
lstrcpy( szSubKey, TEXT("txtfile\\ShellEx\\ContextMenuHandlers\\HelloExtNoAtl"));
lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT,
szSubKey,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&hKey,
&dwDisp);
if( lResult==NOERROR )
{
TCHAR szData[MAX_PATH];
lstrcpy(szData, szCLSID);
lResult = RegSetValueEx( hKey,
NULL,
0,
REG_SZ,
(LPBYTE)szData,
lstrlen(szData) + 1);
RegCloseKey(hKey);
}
else
return SELFREG_E_CLASS;
// register the extension as approved by NT
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx( &osvi );
if( VER_PLATFORM_WIN32_NT == osvi.dwPlatformId )
{
lstrcpy( szSubKey, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"));
lResult = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
szSubKey,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&hKey,
&dwDisp);
if( lResult==NOERROR )
{
TCHAR szData[MAX_PATH];
lstrcpy(szData, TEXT("HelloExtNoAtl"));
lResult = RegSetValueEx( hKey,
szCLSID,
0,
REG_SZ,
(LPBYTE)szData,
lstrlen(szData) + 1);
RegCloseKey(hKey);
}
else
return SELFREG_E_CLASS;
}
return S_OK;
}

