英文:
How to implement Win32 macro MAKEINTRESOURCE
问题
你好,下面是你要求的翻译部分:
你好,我能帮忙实现宏 MAKEINTRESOURCEW 吗?当我查看头文件时,我找到了这个:
#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
我需要这个功能用于我的程序,我正在实现 IContextMenu 功能以查看资源管理器上下文菜单。在我测试的所有项目中,我可以通过 GetCommandString 函数获取命令字符串。但在一个项目中,我无法获取命令字符串。这个项目是“用 Notepad++ 编辑”。所以我在文档中找到了在结构体 [CMINVOKECOMMANDINFOEX][1] 的 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());
[1]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ns-shobjidl_core-cminvokecommandinfoex
英文:
Hi can you help with implementation macro MAKEINTRESOURCEW. When I view to the header file I found this:
#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
I need this functionality for my program where I am implementing the IContextMenu functionality to view explorer context menu. In all my items which I have tested I can get command string with function GetCommandString. But in one item I don't get the command string. And the item is Edit with Notepad++. So I have found in documentation that in the lpverb in structure CMINVOKECOMMANDINFOEX use the id of the item with macro MAKEINTRESOURCE.
But now I don't know how to invoke the command with id of this item and replace this macro.
This is my java code:
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());
答案1
得分: 2
所以我在
CMINVOKECOMMANDINFOEX
结构的lpverb
中的文档中找到了使用宏MAKEINTRESOURCE
和项目的id
请仔细再次阅读 CMINVOKECOMMANDINFOEX
文档,你不需要使用 ID,而是使用偏移量:
lpVerb
类型:
LPCSTR
指向以 null 结尾的字符串的地址,该字符串指定要执行的命令的与语言无关的名称。当应用程序激活命令时,此成员通常是一个字符串。系统为以下命令字符串提供预定义的常量值。
<pre>
常量 命令字符串
CMDSTR_RUNAS "RunAs"
CMDSTR_PRINT "Print"
CMDSTR_PREVIEW "Preview"
CMDSTR_OPEN "Open"
</pre>这不是固定的集合;上下文菜单处理程序可以发明新的规范动词,应用程序可以调用它们。
如果存在规范动词而菜单处理程序没有实现规范动词,则它必须返回失败代码以使下一个处理程序能够处理此动词。不这样做将破坏系统中的功能,包括 ShellExecute。
或者,而不是指针,此参数可以是
MAKEINTRESOURCE(offset)
,其中offset
是要执行的命令的菜单标识符偏移量。 实现可以使用IS_INTRESOURCE
宏来检测是否使用了此替代方法。当用户选择菜单命令时,Shell 就会使用此替代方法。
因此,在使用CMINVOKECOMMANDINFOEX::lpVerb
与MAKEINTRESOURCE()
时,你不应该提供你想要调用的菜单项的实际ID(特别是因为你不知道IContextMenu::QueryContextMenu()
为哪个菜单项分配了哪个ID)。相反,你应该提供菜单项的偏移量(偏移量的值被直接转换为字符指针 - 这就是MAKEINTRESOURCE()
所做的一切)。
在调用IContextMenu::QueryContextMenu()
设置菜单项时,你可以自行控制这些偏移量。这在IContextMenu::InvokeCommand()
文档中有说明:
IContextMenu
接口由几个 Shell 扩展处理程序和命名空间扩展导出。它用于向快捷菜单中添加命令。当用户选择由处理程序或命名空间扩展添加到快捷菜单中的命令之一时,Shell 将调用该命令的InvokeCommand
方法。可以通过其菜单标识符偏移量指定命令,该偏移量在调用IContextMenu::QueryContextMenu
时被定义,或者可以通过其相关联的动词指定命令。 应用程序可以通过获取对象的IContextMenu
接口的指针来直接调用此方法。应用程序还可以通过调用ShellExecute
或ShellExecuteEx
并指定命名空间扩展或处理程序支持的动词来间接调用此方法。
因此,当调用每个处理程序的 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 Explorer Shell 能够做的一样。
英文:
> So I have found in documentation that in the lpverb
in structure CMINVOKECOMMANDINFOEX
use the id of the item with macro MAKEINTRESOURCE
Read the CMINVOKECOMMANDINFOEX
documentation again more carefully, you DO NOT provide it with an ID, but with an OFFSET instead:
> 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.
>
> <pre>
> Constant Command string
> CMDSTR_RUNAS "RunAs"
> CMDSTR_PRINT "Print"
> CMDSTR_PREVIEW "Preview"
> CMDSTR_OPEN "Open"
> </pre>
>
> 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.
So, when using MAKEINTRESOURCE()
with CMINVOKECOMMANDINFOEX::lpVerb
, you are not supposed to provide the actual ID of the menu item you want to invoke (especially since you don't know which ID was assigned to which menu item by IContextMenu::QueryContextMenu()
). You are supposed to provide the offset of the menu item instead (where the value of the offset is type-casted as-is into a character pointer - that is all MAKEINTRESOURCE()
does).
You have some control over those offsets yourself, when you call IContextMenu::QueryContextMenu()
to setup the menu items. This is stated in the IContextMenu::InvokeCommand()
documentation:
> 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.
So, when you call each handler's QueryContextMenu()
method, you have to specify the offset within your own HMENU
where the handler can insert its menu items, and the minimum and maximum range of IDs that the handler can assign to those menu items. That way, when you are later notified via WM_COMMAND
which menu item ID was selected, you can figure out which handler it belongs to by comparing the reported ID to the ranges you specified, and when you find a match then subtract that handler's minimum ID from the reported ID and pass the resulting offset to that handler's InvokeCommand()
method. Or, you can use the handler's GetCommandString()
method to get the verb string of the menu item at that offset and then pass that verb string to InvokeCommand()
.
And just FYI, "Edit with Notepad++"
is the help string reported by Notepad++'s IContextMenu::GetCommandString()
implementation for that menu item, so the fact that Windows Explorer can display that text means Notepad++'s handler works just fine. If you are not able to retrieve that same text when calling GetCommandString()
yourself, then you are not using GetCommandString()
properly. You should be able to display and execute every menu item that is created by every handler's QueryContextMenu()
, just like the standard Windows Explorer Shell can.
答案2
得分: 0
我已找到这个问题的解决方案。我正在为此升级我的代码:
result = User32Ex.INSTANCE.GetMenuItemInfoW(hMenu.getPointer(), 4, true, menuiteminfow.getPointer());
if (!result.booleanValue()) {
int errorCode = Native.getLastError();
System.out.println("错误代码:" + 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 字符串命令。
英文:
I have found the solution of this problem. I am upgrading my code for this:
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());
The key change is that when you want invoke command threw the id and not the command you must define the wId to the lpVerb variable and not to the lpVerbW or both. Because the lpVerbW is for the unicode string command only.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论