Hi I have implementation of the IShellFolder com interface in my .NET program when I have get all items from shell context menu. But I have a problem where my tracking shell context menu have some different items versus explorer shell context menu. In the pictures below you have seen that in my program I do not view Open in program sub menu. I have only one item "Open in program". And in my context menu missing Open in Notepad++ and I have in addition some other items like 7-zip item and submenu and CRC SHA from 7-zip program too.
The first picture is shell context menu from windows explorer and the second picture is from my shell context menu code.
Can you tell me where I have an error? Thank you very much.
And this is my code:
private ContextMenu CreateFileContextMenu(FileInfo[] files, Point location)
{
Win32APICaller.CoInitializeEx(IntPtr.Zero, COINIT.MULTITHREADED);
IShellFolder parentFolder = GetParentFolder(files[0].DirectoryName);
IntPtr[] pidls = this.GetPIDLs(parentFolder, files);
IntPtr pMenu = IntPtr.Zero;
IntPtr iContextMenuPtr = IntPtr.Zero;
IntPtr iContextMenuPtr2 = IntPtr.Zero;
IntPtr iContextMenuPtr3 = IntPtr.Zero;
if (pidls != null)
{
IContextMenu contextMenu;
if (this.GetContextMenuInterfaces(parentFolder, pidls, out contextMenu, out iContextMenuPtr))
{
pMenu = Win32APICaller.CreatePopupMenu();
Marshal.QueryInterface(iContextMenuPtr, ref IID_IContextMenu2, out iContextMenuPtr2);
Marshal.QueryInterface(iContextMenuPtr, ref IID_IContextMenu3, out iContextMenuPtr3);
contextMenu2 = (IContextMenu2)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr2, typeof(IContextMenu2));
contextMenu3 = (IContextMenu3)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr3, typeof(IContextMenu3));
int nResult = contextMenu.QueryContextMenu(pMenu, 0, 1, 30000, CMF.EXPLORE | CMF.CANRENAME | CMF.NORMAL | CMF.INCLUDESTATIC | CMF.EXTENDEDVERBS);
int count = Win32APICaller.GetMenuItemCount(pMenu);
//contextMenu3.QueryContextMenu(pMenu, 0, 1, 30000, /*CMF.EXPLORE | CMF.NORMAL |*/ CMF.EXTENDEDVERBS);
count = Win32APICaller.GetMenuItemCount(pMenu);
Win32APICaller.SendMessage(this.Handle, WM_INITMENUPOPUP, pMenu, 0);
uint nSelected = Win32APICaller.TrackPopupMenuEx(pMenu, 0x0100, location.X, location.Y, this.Handle, IntPtr.Zero);
}
}
}
private IntPtr[] GetPIDLs(IShellFolder parentFolder, FileInfo[] files)
{
if (parentFolder != null)
{
IntPtr[] pidls = new IntPtr[files.Length];
for (int index = 0; index < files.Length; index++)
{
FileInfo fileInfo = files[index];
uint pchEaten = 0;
SFGAO pdwAttributes = 0;
IntPtr pPIDL = IntPtr.Zero;
int nResult = parentFolder.ParseDisplayName(this.Handle, IntPtr.Zero, fileInfo.Name, ref pchEaten, out pPIDL, ref pdwAttributes);
if (nResult == 0)
{
pidls[index] = pPIDL;
}
}
return pidls;
}
return null;
}
private IShellFolder GetParentFolder(string folderName)
{
IShellFolder desktopFolder = this.GetDektopFolder();
if (desktopFolder != null)
{
IntPtr pPIDL = IntPtr.Zero;
uint pchEaten = 0;
SFGAO pdwAttributes = 0;
int nResult = desktopFolder.ParseDisplayName(this.Handle, IntPtr.Zero, folderName, ref pchEaten, out pPIDL, ref pdwAttributes);
if (nResult == 0)
{
IntPtr pStrRet = Marshal.AllocCoTaskMem(260 * 2 + 4);
Marshal.WriteInt32(pStrRet, 0, 0);
nResult = desktopFolder.GetDisplayNameOf(pPIDL, SHGNO.FORPARSING, pStrRet);
StringBuilder strFolder = new StringBuilder(260);
Win32APICaller.StrRetToBuf(pStrRet, pPIDL, strFolder, 260);
Marshal.FreeCoTaskMem(pStrRet);
pStrRet = IntPtr.Zero;
IntPtr pUnknownParentFolder = IntPtr.Zero;
nResult = desktopFolder.BindToObject(pPIDL, IntPtr.Zero, ref IID_IShellFolder, out pUnknownParentFolder);
Marshal.FreeCoTaskMem(pPIDL);
if (nResult == 0)
{
return (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnknownParentFolder, typeof(IShellFolder));
}
}
}
return null;
}
private IShellFolder GetDektopFolder()
{
IntPtr pUnknownDesktopFolder = IntPtr.Zero;
int nResult = Win32APICaller.SHGetDesktopFolder(out pUnknownDesktopFolder);
if (nResult == 0)
return (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnknownDesktopFolder, typeof(IShellFolder));
else
return null;
}
private bool GetContextMenuInterfaces(IShellFolder parentFolder, IntPtr[] pidls, out IContextMenu contextMenu, out IntPtr contextMenuPtr)
{
int nResult = parentFolder.GetUIObjectOf(this.Handle, (uint)pidls.Length, pidls, IID_IContextMenu, IntPtr.Zero, out contextMenuPtr);
contextMenu = null;
if (nResult == 0)
{
contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(contextMenuPtr, typeof(IContextMenu));
return true;
}
return false;
}
protected override void WndProc(ref Message m)
{
if (this.contextMenu3 != null)
{
this.contextMenu3.HandleMenuMsg2((uint)m.Msg, m.WParam, m.LParam, m.Result);
}
else if (this.contextMenu2 != null)
{
this.contextMenu2.HandleMenuMsg((uint)m.Msg, m.WParam, m.LParam);
}
base.WndProc(ref m);
}
In the pictures below you have seen that in my program I do not view Open in program sub menu. I have only one item "Open in program".
The reason for this is that these submenus are delay-generated (which explains why they don’t contain anything interesting when you expand them) and owner-drawn.
So you need to handle messages associated with owner-drawn menu items.
After that you will get what you expected like this:
The following is an Win32 C++ sample code you can refer to:
#define SCRATCH_QCM_FIRST 1
#define SCRATCH_QCM_LAST 0x7FFF
IContextMenu2* g_pcm2;
IContextMenu3* g_pcm3;
//...
void OnContextMenu(HWND hwnd, int xPos, int yPos)
{
WCHAR pszFilePath[] = L"C:\\Users\\me\\Desktop\\test1.txt";
IShellFolder* psfDesktop = NULL;
ITEMIDLIST* id = 0;
LPCITEMIDLIST idChild = 0;
IContextMenu* pcm = NULL;
int iCmdTemp = 0;
POINT pt = { xPos, yPos };
if (pt.x == -1 && pt.y == -1) {
pt.x = pt.y = 0;
ClientToScreen(hwnd, &pt);
}
SHParseDisplayName(pszFilePath, 0, &id, 0, 0);
SHBindToParent(id, IID_IShellFolder, (void**)& psfDesktop, &idChild);
psfDesktop->GetUIObjectOf(hwnd, 1, (const ITEMIDLIST **)&idChild, __uuidof(IContextMenu), NULL, (void **)&pcm);
if (pcm) {
HMENU hmenu = CreatePopupMenu();
if (hmenu) {
if (SUCCEEDED(pcm->QueryContextMenu(hmenu, 0,
SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST,
CMF_NORMAL))) {
pcm->QueryInterface(IID_IContextMenu2, (void**)& g_pcm2);
pcm->QueryInterface(IID_IContextMenu3, (void**)& g_pcm3);
int iCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD,
pt.x, pt.y, hwnd, NULL);
if (g_pcm2) {
g_pcm2->Release();
g_pcm2 = NULL;
}
if (g_pcm3) {
g_pcm3->Release();
g_pcm3 = NULL;
}
if (iCmd > 0) {
CMINVOKECOMMANDINFOEX info = { 0 };
info.cbSize = sizeof(info);
info.fMask = 0x00004000;
info.hwnd = hwnd;
iCmdTemp = iCmd - SCRATCH_QCM_FIRST;
info.lpVerb = MAKEINTRESOURCEA(iCmdTemp);
info.lpVerbW = MAKEINTRESOURCEW(iCmdTemp);
info.nShow = SW_SHOWNORMAL;
pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)& info);
}
}
DestroyMenu(hmenu);
}
pcm->Release();
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int xPos;
int yPos;
if (g_pcm3) {
LRESULT lres;
if (SUCCEEDED(g_pcm3->HandleMenuMsg2(message, wParam, lParam, &lres))) {
return lres;
}
}
else if (g_pcm2) {
if (SUCCEEDED(g_pcm2->HandleMenuMsg(message, wParam, lParam))) {
return 0;
}
}
switch (message)
{
case WM_CONTEXTMENU:
xPos = GET_X_LPARAM(lParam);
yPos = GET_Y_LPARAM(lParam);
OnContextMenu(hWnd, xPos, yPos);
break;
//...