I want to call system context menu from my program.

I do it at first in VC6. It called the context menu of file c:\windows\explorer.exe. It works well. The code is:


//Worked well in VC 6

LPSHELLFOLDER lpDeskFolder;
    SHGetDesktopFolder(&lpDeskFolder);
    if(!lpDeskFolder)
    {
        MessageBox(LPSTR("Fail to get desktop folder!"),
            LPSTR("ccrun's App"), MB_OK | MB_ICONWARNING);
        return;
    }
   
char strFileName[] = "explorer.exe";
char strFilePath[] = "c:\\windows";
int nPathLen = sizeof(strFilePath);
   
    wchar_t wszPath;
    LPITEMIDLIST ParentPidl;
    DWORD dwEaten;
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strFilePath, nPathLen, wszPath, MAX_PATH);

DWORD dwResult =  lpDeskFolder->ParseDisplayName(GetSafeHwnd(),
            0, wszPath, &dwEaten, &ParentPidl, 0);
    if(dwResult != NOERROR)
    {
        MessageBox(LPSTR("Invalid file name!"),
            LPSTR("ccrun's App"), MB_OK | MB_ICONWARNING);
        return;
    }

    // Get the info of IShellFolder
    LPSHELLFOLDER lpParentFolder;
    lpDeskFolder->BindToObject(ParentPidl, 0,
            IID_IShellFolder,(void **)&lpParentFolder);
    if(!lpParentFolder)
    {
        MessageBox(LPSTR("Invalid file name!"),
            LPSTR("ccrun's App"), MB_OK | MB_ICONWARNING);
        lpDeskFolder->Release();
        lpParentFolder->Release();
        return;
    }
    LPITEMIDLIST lpItemIDList;

    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strFileName, sizeof(strFileName), wszPath, MAX_PATH);
    lpParentFolder->ParseDisplayName(GetSafeHwnd(),
            0, wszPath, &dwEaten, &lpItemIDList, 0);

    // Get the context menu of files
    LPCONTEXTMENU lpContextMenu;
    lpParentFolder->GetUIObjectOf(GetSafeHwnd(), 1,
           (LPCITEMIDLIST *)&lpItemIDList, IID_IContextMenu,
           0, (void**)&lpContextMenu);
    if(!lpContextMenu)
    {
        MessageBox("Fail to get the interface of context menu!",
            "ccrun's App", MB_OK | MB_ICONWARNING);
        lpDeskFolder->Release();
        lpParentFolder->Release();
        return;
    }
 
    CMINVOKECOMMANDINFO ici;
    ZeroMemory(&ici, sizeof(ici));
    ici.cbSize = sizeof(CMINVOKECOMMANDINFO);
    ici.hwnd = GetSafeHwnd();

    // Create context menu
    HMENU hMenu = CreatePopupMenu();
    dwResult = CMF_EXPLORE;
    lpContextMenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF, dwResult);

    // Display the menu
    POINT pt;
    GetCursorPos(&pt);
    int Cmd = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON |
            TPM_RIGHTBUTTON | TPM_RETURNCMD,
            pt.x, pt.y, 0, GetSafeHwnd(), 0);
    ici.lpVerb = MAKEINTRESOURCE(Cmd - 1);
    ici.lpParameters = "";
    ici.lpDirectory = "";
    ici.nShow = SW_SHOWNORMAL;
    lpContextMenu->InvokeCommand(&ici);

    lpContextMenu->Release();
    lpParentFolder->Release();
    lpDeskFolder->Release();


I translated the code to masm32,  but I get following error messages:

  Assembling: D:\masm32\works\contextmenu\3.asm
D:\masm32\works\contextmenu\3.asm(48) : error A2006: undefined symbol : ParseDisplayName
D:\masm32\works\contextmenu\3.asm(63) : error A2006: undefined symbol : BindToObject
D:\masm32\works\contextmenu\3.asm(69) : error A2006: undefined symbol : ParseDisplayName
D:\masm32\works\contextmenu\3.asm(84) : error A2006: undefined symbol : ParseDisplayName
D:\masm32\works\contextmenu\3.asm(92) : error A2006: undefined symbol : GetUIObjectOf
D:\masm32\works\contextmenu\3.asm(110) : error A2006: undefined symbol : g_lpContextMenu
D:\masm32\works\contextmenu\3.asm(114) : error A2044: invalid character in file
D:\masm32\works\contextmenu\3.asm(116) : error A2008: syntax error : ax
D:\masm32\works\contextmenu\3.asm(121) : error A2006: undefined symbol : g_lpCon
textMenu
_
Assembly Error


My code in masm32 is:

.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\shell32.inc
include \masm32\com\include\shlobj.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\shell32.lib
;includelib \masm32\com\colib\shlobj.lib
includelib \masm32\lib\user32.lib

.data
g_szFailGetDesktopFolder db  "Fail to get desktop folder!", 0
g_szInvalidFileName db "Invalid filename!", 0
g_szFailToGetMenuInterface db "Fail to get the interface of context menu!", 0
g_szFileName db "explorer.exe", 0
g_dwPathLen dword sizeof g_szFileName
g_szFilePath db "c:\\windows", 0
   
g_wszPath dw MAX_PATH dup (?)
g_dwEaten dword ?
g_lpDeskFolder LPSHELLFOLDER ?
g_lpParentFolder LPSHELLFOLDER ?
g_ParentPidl ITEMIDLIST <>
g_lpItemIDList dword ?
g_lpContextMenu LPCONTEXTMENU <>
g_ici CMINVOKECOMMANDINFO <>
g_hMenu HANDLE ?
g_wTmp word ?

.code
start:
    invoke SHGetDesktopFolder, ADDR g_lpDeskFolder
    cmp eax, NOERROR
    je @F

    invoke MessageBox, NULL, ADDR g_szFailGetDesktopFolder, ADDR g_szFailGetDesktopFolder, MB_OK + MB_ICONSTOP
    xor ecx, ecx
    jecxz @exit
@@:   
    invoke MultiByteToWideChar, CP_ACP, MB_PRECOMPOSED, ADDR g_szFilePath, ADDR g_dwPathLen, ADDR g_wszPath, MAX_PATH

    push ebx
    mov ebx, offset g_lpDeskFolder
    mov ebx,
    invoke (IShellFolder ptr ).ParseDisplayName, NULL, 0, g_wszPath, ADDR g_dwEaten, ADDR g_ParentPidl, 0
    pop ebx

    cmp eax, NOERROR
    je @F

    invoke MessageBox, NULL, ADDR g_szInvalidFileName, ADDR g_szInvalidFileName, MB_OK + MB_ICONSTOP
    xor ecx, ecx
    jecxz @exit
@@:
    ; get info of IShellFolder
    ;-----------------------------
    push ebx
    mov ebx, offset g_lpDeskFolder
    mov ebx,
    invoke (IShellFolder ptr ).BindToObject, g_ParentPidl, 0, IID_IShellFolder, ADDR g_lpParentFolder
    pop ebx

    push ebx
    mov ebx, offset g_lpDeskFolder
    mov ebx,
    invoke (IShellFolder ptr ).ParseDisplayName, NULL, 0, g_wszPath, ADDR g_dwEaten, ADDR g_ParentPidl, 0
    pop ebx

    cmp eax, NOERROR
    je @F

    invoke MessageBox, NULL, ADDR g_szInvalidFileName, ADDR g_szInvalidFileName, MB_OK + MB_ICONSTOP
    xor ecx, ecx
    jecxz @exit
@@:   
    invoke MultiByteToWideChar, CP_ACP, MB_PRECOMPOSED, ADDR g_szFileName, SIZEOF g_szFileName, ADDR g_wszPath, MAX_PATH

    push ebx
    mov ebx, offset g_lpDeskFolder
    mov ebx,
    invoke (IShellFolder ptr ).ParseDisplayName, NULL, 0, ADDR g_wszPath, ADDR g_dwEaten, ADDR g_lpItemIDList, 0
    pop ebx

    ; Get the context menu
    ;-----------------------
    push ebx
    mov ebx, offset g_lpDeskFolder
    mov ebx,
    invoke (IShellFolder ptr ).GetUIObjectOf, NULL, 1,  ADDR g_lpItemIDList, IID_IContextMenu, 0, ADDR g_lpContextMenu
    pop ebx

    cmp eax, NOERROR; g_lpDeskFolder
    je @F

    invoke MessageBox, NULL, ADDR g_szFailToGetMenuInterface, ADDR g_szFailToGetMenuInterface, MB_OK + MB_ICONSTOP
    xor ecx, ecx
    jecxz @exit
@@:   
    invoke RtlZeroMemory, ADDR g_ici, sizeof g_ici
    mov g_ici.cbSize, sizeof CMINVOKECOMMANDINFO
    ;mov g_ici.hwnd, hDlog

    ;Create context menu
    ;---------------------
    invoke CreatePopupMenu
    mov g_hMenu, eax
    invoke g_lpContextMenu.QueryContextMenu, ghMenu, 0, 1, 0x7FFF, CMF_EXPLORE

    ;Show the menu
    ;---------------------
    invoke TrackPopupMenu, g_hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD, 5, 5, 0, NULL, 0
    dec eax
    MAKEINTRESOURCE ax
    mov g_ici.lpVerb, eax
    mov g_ici.lpParameters, NULL
    mov g_ici.lpDirectory, NULL
    mov g_ici.nShow, SW_SHOWNORMAL
    invoke g_lpContextMenu.InvokeCommand, ADDR g_ici
@exit:
    invoke ExitProcess,NULL

end start



Why they do not recognize the method of IShellFolder?

Thanks...
Posted on 2005-11-26 03:23:53 by purpleendurer
mov ebx,g_lpDeskFolder
mov ebx,
invoke (IShellFolder ptr ).ParseDisplayName, NULL, 0, ADDR g_wszPath, ADDR g_dwEaten, ADDR g_lpItemIDList, 0

should be
coinvoke g_lpDeskFolder,IShellFolder,ParseDisplayName,NULL,0,addr g_wsz...


or in a more direct approach
mov edx,g_lpDeskFolder
mov edx,
Scall dword ptr,g_lpDeskFolder,NULL,0,addr g_wsz.....

or in your style
mov edx,g_lpDeskFolder
mov edx,
invoke (IShellFolder ptr ).IShellFolder_ParseDisplayName,g_lpDeskFolder,NULL, 0, ADDR g_wszPath, ADDR g_dwEaten, ADDR g_lpItemIDList, 0
Posted on 2005-11-26 07:57:31 by Ultrano
Ultrano, thank you...

I tried:

    mov edx, g_lpDeskFolder
    mov edx,
    invoke (IShellFolder ptr ).IShellFolder_ParseDisplayName, NULL, 0, g_wszPath, ADDR g_dwEaten, ADDR ParentPidl, 0


It works now...

May I not need to push and pop value of edx?

Now I has a new question. That is:

.data
g_ParentPidl LPITEMIDLIST ?

.code
....
    mov edx, g_lpDeskFolder
    mov edx,

    ;lpDeskFolder->BindToObject(g_ParentPidl, 0, IID_IShellFolder,(void **)&lpParentFolder);

    invoke (IShellFolder ptr ).IShellFolder_BindToObject, g_ParentPidl, NULL, ADDR IID_IShellFolder, ADDR g_lpParentFolder


I got some error messages when I Assemble & Link it. They are:

Assembling: D:\masm32\works\contextmenu\3.asm
D:\masm32\works\contextmenu\3.asm(26) : error A2008: syntax error : LPITEMIDLIST
D:\masm32\works\contextmenu\3.asm(67) : error A2137: too few arguments to INVOKE
D:\masm32\works\contextmenu\3.asm(67) : error A2006: undefined symbol : g_ParentPidl
D:\masm32\works\contextmenu\3.asm(67) : error A2114: INVOKE argument type mismatch : argument : 1

How to define a variable of  LPITEMIDLIST type?

Regards,
purpleendurer




Posted on 2005-11-27 09:27:34 by purpleendurer
Hello,

You don't need to preserve the value in edx, since the call would probably destroy the value in it.
Posted on 2005-11-27 09:33:42 by roticv
Define the variable like this:
g_ParentPidl dd  ? ; pointer to ITEMIDLIST

In assembler, you needn't specify what type of pointer the variable is - it's always a 4-byte value. In order not to lose track of what this variable points to, I usually put a comment like the one above.

roticv is right, you don't need to preserve EDX when you use "coinvoke". But just remember that it has been filled with "strange" value :) . Also ECX will be filled with another "strange" value. (I mean, do not rely on them being preserved).

Also, I should note that you must never use EDX as a parameter to a "coinvoke". For instance:

mov edx,1
coinvoke pObject,ISomeObject,SomeMethod,0,0,EDX

This is because EDX's value will be overwritten by "coinvoke" .
Posted on 2005-11-27 10:02:16 by Ultrano
Thanks, Ultrano and  roticv...

I keeps your words in my mind..

now I have some new questions.

I modify my codes as:

.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\shell32.inc
include \masm32\com\include\shlobj.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\shell32.lib
;includelib \masm32\com\colib\shlobj.lib
includelib \masm32\lib\user32.lib

.data
g_szFailGetDesktopFolder db  "Fail to get desktop folder!", 0
g_szInvalidFileName db "Invalid filename!", 0
g_szFailToGetMenuInterface db "Fail to get the interface of context menu!", 0
g_szFileName db "explorer.exe", 0
g_dwPathLen dword sizeof g_szFileName
g_szFilePath db "c:\\windows", 0
   
g_wszPath dw MAX_PATH dup (?)
g_dwEaten dword ?
g_lpDeskFolder LPSHELLFOLDER ?
g_lpParentFolder LPSHELLFOLDER ?
g_ParentPidl dd ? ; Pointer to ITEMIDLIST
g_lpItemIDList dd ?
g_lpContextMenu dd ?; Pointer to LPCONTEXTMENU
g_ici CMINVOKECOMMANDINFO <>
g_hMenu HANDLE ?

IID_IShellFolder GUID sIID_IShellFolder
IID_IContextMenu GUID sIID_IContextMenu

.code
start:
    invoke SHGetDesktopFolder, ADDR g_lpDeskFolder
    cmp eax, NOERROR
    je @F

    invoke MessageBox, NULL, ADDR g_szFailGetDesktopFolder, ADDR g_szFailGetDesktopFolder, MB_OK + MB_ICONSTOP
    xor ecx, ecx
    jecxz @exit
@@:   
    invoke MultiByteToWideChar, CP_ACP, MB_PRECOMPOSED, ADDR g_szFilePath, ADDR g_dwPathLen, ADDR g_wszPath, MAX_PATH

    ;push edx
    mov edx, g_lpDeskFolder
    mov edx,
    invoke (IShellFolder ptr ).IShellFolder_ParseDisplayName, g_lpDeskFolder, NULL, 0, ADDR g_wszPath, ADDR g_dwEaten, ADDR g_lpItemIDList, 0
    ;pop edx

    cmp eax, NOERROR
    je @F

    invoke MessageBox, NULL, ADDR g_szInvalidFileName, ADDR g_szInvalidFileName, MB_OK + MB_ICONSTOP
    xor ecx, ecx
    jecxz @exit
@@:
    ; get info of IShellFolder
    ;-----------------------------
    mov edx, g_lpDeskFolder
    mov edx,
    ;lpDeskFolder->BindToObject(g_ParentPidl, 0, IID_IShellFolder,(void **)&lpParentFolder)
    invoke (IShellFolder ptr ).IShellFolder_BindToObject, g_ParentPidl, NULL, ADDR IID_IShellFolder, ADDR g_lpParentFolder

    mov edx, g_lpDeskFolder
    mov edx,
    invoke (IShellFolder ptr ).IShellFolder_ParseDisplayName, NULL, 0, ADDR g_wszPath, ADDR g_dwEaten, ADDR g_ParentPidl, 0

    cmp eax, NOERROR
    je @F

    invoke MessageBox, NULL, ADDR g_szInvalidFileName, ADDR g_szInvalidFileName, MB_OK + MB_ICONSTOP
    xor ecx, ecx
    jecxz @exit
@@:   
    invoke MultiByteToWideChar, CP_ACP, MB_PRECOMPOSED, ADDR g_szFileName, SIZEOF g_szFileName, ADDR g_wszPath, MAX_PATH

    mov edx, g_lpDeskFolder
    mov edx,
    invoke (IShellFolder ptr ).IShellFolder_ParseDisplayName, NULL, 0, ADDR g_wszPath, ADDR g_dwEaten, ADDR g_lpItemIDList, 0

    ; Get the context menu
    ;-----------------------
    mov edx, g_lpDeskFolder
    mov edx,
    invoke (IShellFolder ptr ).IShellFolder_GetUIObjectOf, NULL, 1,  ADDR g_lpItemIDList, IID_IContextMenu, 0, ADDR g_lpContextMenu

    cmp eax, NOERROR; g_lpDeskFolder
    je @F

    invoke MessageBox, NULL, ADDR g_szFailToGetMenuInterface, ADDR g_szFailToGetMenuInterface, MB_OK + MB_ICONSTOP
    xor ecx, ecx
    jecxz @exit
@@:   
    invoke RtlZeroMemory, ADDR g_ici, sizeof g_ici
    mov g_ici.cbSize, sizeof CMINVOKECOMMANDINFO
    ;mov g_ici.hwnd, hDlog

    ; Create context menu
    ;---------------------
    invoke CreatePopupMenu
    mov g_hMenu, eax
    mov edx, g_lpContextMenu
    mov edx,
    ;invoke g_lpContextMenu.QueryContextMenu, g_hMenu, 0, 1, 0x7FFF, CMF_EXPLORE
    invoke (IContextMenu ptr ).IContextMenu_QueryContextMenu, g_hMenu, 0, 1, 07FFFh, CMF_EXPLORE


    ; Show the menu
    ;---------------------
    invoke TrackPopupMenu, g_hMenu, TPM_LEFTALIGN or TPM_LEFTBUTTON or TPM_RIGHTBUTTON or TPM_RETURNCMD, 5, 5, 0, NULL, 0
    dec eax
    MAKEINTRESOURCE ax
    mov g_ici.lpVerb, eax
    mov g_ici.lpParameters, NULL
    mov g_ici.lpDirectory, NULL
    mov g_ici.nShow, SW_SHOWNORMAL
    mov edx, g_lpContextMenu
    mov edx,
    ;invoke g_lpContextMenu.InvokeCommand, ADDR g_ici
    invoke (IContextMenu ptr ).IContextMenu_InvokeCommand, ADDR g_ici
@exit:
    invoke ExitProcess,NULL

end start


I got some error messages when I Assemble & Link it. They are:

Assembling: D:\masm32\works\contextmenu\3.asm
D:\masm32\works\contextmenu\3.asm(66) : error A2137: too few arguments to INVOKE

D:\masm32\works\contextmenu\3.asm(72) : error A2137: too few arguments to INVOKE

D:\masm32\works\contextmenu\3.asm(87) : error A2137: too few arguments to INVOKE

D:\masm32\works\contextmenu\3.asm(95) : error A2137: too few arguments to INVOKE

D:\masm32\works\contextmenu\3.asm(95) : error A2114: INVOKE argument type mismatch : argument : 4
D:\masm32\works\contextmenu\3.asm(116) : error A2137: too few arguments to INVOKE
D:\masm32\works\contextmenu\3.asm(116) : error A2206: missing operator in expression
D:\masm32\works\contextmenu\3.asm(116) : error A2114: INVOKE argument type mismatch : argument : 4
D:\masm32\works\contextmenu\3.asm(123) : error A2008: syntax error : ax
D:\masm32\works\contextmenu\3.asm(131) : error A2137: too few arguments to INVOKE
_
Assembly Error


I give right argument numbers to invoke, but recieve "rror A2137: too few arguments to INVOKE"

I donot understand it


Regards,
purpleendurer
Posted on 2005-11-28 00:27:53 by purpleendurer
Instead of
invoke (IShellFolder ptr ).IShellFolder_BindToObject, g_ParentPidl, NULL, ADDR IID_IShellFolder, ADDR g_lpParentFolder


it should be
invoke (IShellFolder ptr ).IShellFolder_BindToObject,g_lpDeskFolder, g_ParentPidl, NULL, ADDR IID_IShellFolder, ADDR g_lpParentFolder


If I am not wrong. Just modify every line that has this mistake.
Posted on 2005-11-28 00:31:43 by roticv
Just to explain why we need that 1 extra parameter in calling COM objects:
there is always 1 "hidden" parameter, called _this. In COM objects, it is passed as the first parameter on stack. Thus
void SomeCOM::DoIt(long X){}
is actually:

void SomeCOM::DoIt(SomeCOM* _this, long X){}

Posted on 2005-11-28 07:57:17 by Ultrano
thanks roticv and Ultrano

roticv is right..
I did not note the code Ultrano gived first

I missed g_lpDeskFolder parameter at invoke the method of IShellFolder
and missed g_lpContextMenu parameter at invoke the method of IContextMenu

Thank Ultrano for your explain....


I searched a early relative topic:

Calling shell context menu from my program in COM
http://www.asmcommunity.net/board/index.php?topic=17772.0

It is useful for me, I am studying it...


Regards,
purpleendurer
Posted on 2005-11-30 00:26:20 by purpleendurer
Hi, everybody...

With the great help of Ultrano and roticv, now my program can masm.link and run successfully(the source files are in attach)

But I have a new question...

I has passed  the CMF_EXPLORE flag:

                        invoke (IContextMenu ptr ).IContextMenu_QueryContextMenu, g_lpContextMenu, g_hMenu, 0, 1, 07FFFh, CMF_EXPLORE



but the context menu of my program  called is not  CMF_EXPLORE (a context menu with CMF_EXPLORE flag  includes the extend menu item by WinRAR and other programs),  it is  like CMF_DEFAULTONLY at all(a context menu with CMF_DEFAULTONLY flag does not include  the extend menu item by WinRAR and other programs).


I can not find out the reason?Who can give me some directions?

Regards,
purpleendurer
Attachments:
Posted on 2005-12-01 09:38:50 by purpleendurer
I try to find the reason in VC6 program.

I ceated a new MFC win32 program in VC6, and used the calling context menu code  I listed above.

and the context menu this program called is still with CMF_DEFAULTONLY flag, like the one I has attached asm program called.  It is:



But uses the same code in a VC MFC program using flash.ocx to play flash file, the context menu be called is with CMF_EXPLORE flag. It is:



So if I want to call a context menu  with CMF_EXPLORE flag, I must use a ActiveX/COM object in my program?

Regards,
purpleendurer
Attachments:
Posted on 2005-12-02 23:00:52 by purpleendurer
Hi, everybody....

I think I found the way to call context menu with CMF_EXPLORE flag.

That is invoke CoInitialize() before call context menu.....

In my attached code,  need to  add 3 statements only, They are:



include \masm32\include\ole32.inc ; Add this inc file
.....
includelib \masm32\lib\ole32.lib       ;Add this lib file


DlgProc proc hDlg: HWND,  uMsg: UINT,  wParam: WPARAM,  lParam: LPARAM
      .IF uMsg == WM_INITDIALOG
            m_m2m g_hDlg, hDlg

            invoke CoInitialize, NULL              ; add this instruction

      .ELSEIF uMsg == WM_CLOSE



But there is a question, it is the "send to" item of context menu does not work.

Regards,
purpleendurer
Posted on 2005-12-06 00:10:19 by purpleendurer
Those are things beyond my scope of knowledge and experience, that's why I'm quiet ^^"  -just to let you know
Posted on 2005-12-06 01:29:36 by Ultrano
Ultrano, you are modest...

You had given me great helps....

Thank you...
Posted on 2005-12-15 09:01:37 by purpleendurer