如何实现Win32宏MAKEINTRESOURCE

How to implement Win32 macro MAKEINTRESOURCE

您好,您能帮助实现宏 MAKEINTRESOURCEW 吗?当我查看头文件时,我发现了这个:

#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))

我的程序需要此功能,我正在实现 IContextMenu 功能以查看资源管理器上下文菜单。在我测试过的所有项目中,我都可以使用函数 GetCommandString 获取命令字符串。但是在一项中我没有得到命令字符串。该项目是使用 Notepad++ 编辑。所以我在文档中发现,在结构 CMINVOKECOMMANDINFOEX 的 lpverb 中,使用带有宏 MAKEINTRESOURCE 的项目的 id。

但是我现在不知道如何调用这个id的命令来替换这个宏

这是我的 java 代码:

Memory numberVerb = new Memory(Native.POINTER_SIZE);
numberVerb.setLong(0, 109);

CMINVOKECOMMANDINFOEX cmInvokeCommandEx = new CMINVOKECOMMANDINFOEX();          
cmInvokeCommandEx.cbSize = cmInvokeCommandEx.size();                            
cmInvokeCommandEx.fMask = 0x00004000;                                           
cmInvokeCommandEx.hwnd = null;                                                                         
cmInvokeCommandEx.lpVerbW = new WTypes.LPWSTR(numberVerb);                      
cmInvokeCommandEx.lpVerb = new WTypes.LPSTR(verb);                              
cmInvokeCommandEx.lpParameters = null;                                          
cmInvokeCommandEx.lpParametersW = null;                                         
cmInvokeCommandEx.lpDirectory = new WTypes.LPSTR(parentDir.getAbsolutePath());  
cmInvokeCommandEx.lpDirectoryW = new WTypes.LPWSTR(parentDir.getAbsolutePath());
cmInvokeCommandEx.nShow = 5;                                                    
cmInvokeCommandEx.dwHotKey = 0;                                                 
cmInvokeCommandEx.hIcon = Pointer.NULL;                                         
cmInvokeCommandEx.lpTitle = null;                                               
cmInvokeCommandEx.lpTitleW = null;                                              
cmInvokeCommandEx.ptInvoke = point;                                             
cmInvokeCommandEx.write();                                                      
                                                                            
hResult = contextMenu2.InvokeCommand(cmInvokeCommandEx.getPointer());           

So I have found in documentation that in the lpverb in structure CMINVOKECOMMANDINFOEX use the id of the item with macro MAKEINTRESOURCE

再次仔细阅读 CMINVOKECOMMANDINFOEX 文档,您不提供 ID,而是提供 OFFSET相反:

lpVerb

Type: LPCSTR

The address of a null-terminated string that specifies the language-independent name of the command to carry out. This member is typically a string when a command is being activated by an application. The system provides predefined constant values for the following command strings.

Constant          Command string
CMDSTR_RUNAS      "RunAs"
CMDSTR_PRINT      "Print"
CMDSTR_PREVIEW    "Preview"
CMDSTR_OPEN       "Open"

This is not a fixed set; new canonical verbs can be invented by context menu handlers and applications can invoke them.

If a canonical verb exists and a menu handler does not implement the canonical verb, it must return a failure code to enable the next handler to be able to handle this verb. Failing to do this will break functionality in the system including ShellExecute.

Alternatively, rather than a pointer, this parameter can be MAKEINTRESOURCE(offset) where offset is the menu-identifier offset of the command to carry out. Implementations can use the IS_INTRESOURCE macro to detect that this alternative is being employed. The Shell uses this alternative when the user chooses a menu command.

因此,当 MAKEINTRESOURCE()CMINVOKECOMMANDINFOEX::lpVerb 一起使用时,您不应提供要调用的菜单项的实际 ID(特别是因为您不知道分配了哪个 ID通过 IContextMenu::QueryContextMenu() 到哪个菜单项)。您应该改为提供菜单项的 offset(偏移量的 value 为 type-casted as-is变成一个字符指针——这就是所有 MAKEINTRESOURCE() 所做的)。

当您调用 IContextMenu::QueryContextMenu() 设置菜单项时,您可以自己控制这些偏移量。这在 IContextMenu::InvokeCommand() 文档中有说明:

The IContextMenu interface is exported by several Shell extension handlers and namespace extensions. It is used to add commands to shortcut menus. When the user selects one of the commands that the handler or namespace extension added to a shortcut menu, the Shell calls that command's InvokeCommand method. The command can be specified by its menu identifier offset, defined when IContextMenu::QueryContextMenu was called, or by its associated verb. An application can invoke this method directly by obtaining a pointer to an object's IContextMenu interface. An application can also invoke this method indirectly by calling ShellExecute or ShellExecuteEx and specifying a verb that is supported by the namespace extension or handler.

因此,当您调用每个处理程序的 QueryContextMenu() 方法时,您必须在自己的 HMENU 中指定处理程序可以插入其菜单项的偏移量,以及 ID 的最小和最大范围处理程序可以分配给那些菜单项。这样,当稍后通过 WM_COMMAND 通知您选择了哪个菜单项 ID 时,您可以通过将报告的 ID 与您指定的范围进行比较来确定它属于哪个处理程序,当您找到匹配项时,然后减去它从报告的 ID 中获取处理程序的最小 ID,并将生成的偏移量传递给该处理程序的 InvokeCommand() 方法。或者,您可以使用处理程序的 GetCommandString() 方法获取该偏移量处菜单项的动词字符串,然后将该动词字符串传递给 InvokeCommand().

仅供参考,"Edit with Notepad++" 是 Notepad++ 的 IContextMenu::GetCommandString() 实现针对该菜单项报告的帮助字符串,因此 Windows Explorer 可以显示该文本这一事实意味着 Notepad++ 的处理程序有效正好。如果您自己调用 GetCommandString() 时无法检索到相同的文本,那么您没有正确使用 GetCommandString()。您应该能够显示和执行由每个处理程序的 QueryContextMenu() 创建的 每个 菜单项,就像标准的 Windows 资源管理器 Shell 一样。

我找到了解决这个问题的方法。我正在为此升级我的代码:

result = User32Ex.INSTANCE.GetMenuItemInfoW(hMenu.getPointer(), 4, true, menuiteminfow.getPointer());
if (!result.booleanValue()) {
   int errorCode = Native.getLastError();
   System.out.println("Error Code: " + errorCode);
   return false;
}

CMINVOKECOMMANDINFOEX cmInvokeCommandEx = new CMINVOKECOMMANDINFOEX();          
cmInvokeCommandEx.cbSize = cmInvokeCommandEx.size();                            
cmInvokeCommandEx.fMask = 0x00004000;                                           
cmInvokeCommandEx.hwnd = null;                                                                         
cmInvokeCommandEx.lpVerbW = new WTypes.LPWSTR(new Pointer(menuiteminfow.wId - 1));                      
cmInvokeCommandEx.lpVerb = new WTypes.LPSTR(new Pointer(menuiteminfow.wId - 1));                              
cmInvokeCommandEx.lpParameters = null;                                          
cmInvokeCommandEx.lpParametersW = null;                                         
cmInvokeCommandEx.lpDirectory = new WTypes.LPSTR(parentDir.getAbsolutePath());  
cmInvokeCommandEx.lpDirectoryW = new WTypes.LPWSTR(parentDir.getAbsolutePath());
cmInvokeCommandEx.nShow = 5;                                                    
cmInvokeCommandEx.dwHotKey = 0;                                                 
cmInvokeCommandEx.hIcon = Pointer.NULL;                                         
cmInvokeCommandEx.lpTitle = null;                                               
cmInvokeCommandEx.lpTitleW = null;                                              
cmInvokeCommandEx.ptInvoke = point;                                             
cmInvokeCommandEx.write();                                                      
                                                                        
hResult = contextMenu2.InvokeCommand(cmInvokeCommandEx.getPointer());

关键的变化是,当你想要调用命令抛出 id 而不是命令时,你必须将 wId 定义到 lpVerb 变量而不是 lpVerbW 或两者。因为 lpVerbW 仅用于 unicode 字符串命令。