我正在构建一个 POC,可以将新的主菜单项注入到任何 Windows 应用程序中,然后让这些自定义菜单项调用自定义逻辑。
到目前为止,我已经注入了带有子项的新菜单,并在引发 EVENT_SYSTEM_MENUEND 事件时触发 SetWinEventHook 处理程序。我可以在处理程序中执行自定义代码,但问题是我不知道单击了哪个菜单项,因此我无法执行选择特定菜单项时需要运行的逻辑。
我拥有的代码片段 -
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwflags);
private static readonly WinEventDelegate callback = new WinEventDelegate(EventCallback);
添加自定义菜单
var newMenu = CreateMenu();
InsertMenu(newMenu, 0, MF_BYPOSITION, 10001, "Item 1");
InsertMenu(newMenu, 1, MF_BYPOSITION, 10002, "Item 2");
AppendMenu(menu, MF_POPUP, newMenu, "My Menu Section");
然后我正在设置挂钩
public void SetEventHook()
{
var result = SetWinEventHook(EVENT_SYSTEM_MENUEND, EVENT_SYSTEM_MENUEND, IntPtr.Zero, callback, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD);
Console.WriteLine($"SetWinEventHook returned {result}");
}
最后是单击菜单项时触发的委托处理程序
public static void EventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Process.Start("mspaint");
}
我遇到的问题是,当 EventCallback 触发时,没有任何东西可以帮助我识别选择了哪个菜单项。我认为 idObject 参数将具有所选菜单项的 id,但它总是返回 -3,无论选择哪个菜单项(自定义或已存在)。
有没有一种方法可以让我知道选择了哪个菜单项并使用它来驱动处理程序内的自定义逻辑?
使用处理常规 UI 自动化事件的处理程序,我可以将其添加到 Visual Studio 的菜单项元素上(WPF FrameworkId)。具有 XAML FrameworkId 的应用程序需要额外的努力。 辅助工具 - 检查在执行此操作时很有帮助。
int Element(IUIAutomation* automation)
{
// Get the element under the cursor
// Use GetPhysicalCursorPos to interact properly with
// High DPI
POINT pt;
GetPhysicalCursorPos(&pt);
IUIAutomationElement* pAtMouse;
HRESULT hr = automation->ElementFromPoint(pt, &pAtMouse);
if (FAILED(hr))
return hr;
// Get the element's name and print it
BSTR name;
hr = pAtMouse->get_CurrentName(&name);
if (SUCCEEDED(hr))
{
wprintf(L"Element's Name: %s \n", name);
SysFreeString(name);
VARIANT v;
pAtMouse->GetCurrentPropertyValue(UIA_IsInvokePatternAvailablePropertyId,&v);
if (v.boolVal)
{
pAtMouse->GetCurrentPropertyValue(UIA_FrameworkIdPropertyId,&v);
CONTROLTYPEID id{};
pAtMouse->get_CurrentControlType(&id);
IUIAutomationInvokePattern* pattern;
pAtMouse->GetCurrentPatternAs(UIA_InvokePatternId, IID_PPV_ARGS(&pattern));
EventHandler* pEHTemp = new EventHandler();
HRESULT h = automation->AddAutomationEventHandler(UIA_Invoke_InvokedEventId, pAtMouse, TreeScope_Element, NULL, (IUIAutomationEventHandler*)pEHTemp);
if (h == S_OK) {
}
pEHTemp->Release();
pattern->Release();
}
}
// Clean up our COM pointers
pAtMouse->Release();
return hr;
}
int main(int argc, TCHAR* argv[])
{
// Initialize COM and create the main Automation object
IUIAutomation* g_pAutomation;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL,
CLSCTX_INPROC_SERVER, __uuidof(IUIAutomation),
(void**)&g_pAutomation);
if (FAILED(hr))
return (hr);
bool quit = false;
while (!quit)
{
SHORT leftControlMod = GetAsyncKeyState(VK_LCONTROL);
if (leftControlMod != 0)
{
Element(g_pAutomation);
}
quit = GetAsyncKeyState(VK_ESCAPE);
}
g_pAutomation->RemoveAllEventHandlers();
g_pAutomation->Release();
CoUninitialize();
return 0;
}