@@ -267,3 +267,130 @@ void mb_shell::context_menu_hooks::install_SHCreateDefaultContextMenu_hook() {
267267 return res;
268268 });
269269}
270+
271+ #pragma optimize("", off)
272+ extern " C" __declspec(dllexport) size_t
273+ functionToGetGetUIObjectOfVptr (IShellFolder2 *i) {
274+ return i->GetUIObjectOf (nullptr , 0 , nullptr , IID_IContextMenu, nullptr ,
275+ nullptr );
276+ }
277+ #pragma optimize("", on)
278+ void mb_shell::context_menu_hooks::install_GetUIObjectOf_hook () {
279+ // For OneCommander
280+
281+ auto proc = blook::Process::self ();
282+ auto shell32 = proc->module (" shell32.dll" );
283+ auto SHELL32_SHCreateDefaultContextMenu =
284+ shell32.value ()->exports (" SHELL32_SHCreateDefaultContextMenu" );
285+
286+ static std::atomic_bool close_next_create_window_exw_window = false ;
287+
288+ IShellFolder *psfDesktop = NULL ;
289+ IShellFolder2 *psf2Desktop = NULL ;
290+ if (NOERROR != SHGetDesktopFolder (&psfDesktop)) {
291+ std::cerr << " Failed to get desktop shell folder" << std::endl;
292+ return ;
293+ }
294+ psfDesktop->QueryInterface (IID_IShellFolder2, (void **)&psf2Desktop);
295+ psfDesktop->Release ();
296+
297+ // patch 'call' in functionToGetGetUIObjectOfVptr to 'mov rax, ptr' to get
298+ // the real function called
299+ auto disasmFuncGetVptr =
300+ blook::Pointer ((void *)&functionToGetGetUIObjectOfVptr)
301+ .range_size (0x50 )
302+ .disassembly ();
303+
304+ std::optional<size_t > stackSize;
305+ for (auto &inst : disasmFuncGetVptr) {
306+ if (inst->getMnemonic () == zasm::x86::Mnemonic::Sub && !stackSize)
307+ stackSize = inst->getOperand (1 ).get <zasm::Imm>().value <size_t >();
308+
309+ if (inst->getMnemonic () == zasm::x86::Mnemonic::Call) {
310+ inst.ptr ()
311+ .reassembly ([&](zasm::x86::Assembler a) {
312+ a.mov (zasm::x86::rax, inst->getOperand (0 ).get <zasm::Mem>());
313+ a.add (zasm::x86::rsp, *stackSize);
314+ a.ret ();
315+ })
316+ .patch ();
317+ break ;
318+ }
319+ }
320+
321+ auto pGetUIObjectOf =
322+ blook::Pointer ((void *)functionToGetGetUIObjectOfVptr (psf2Desktop));
323+ std::println (" GetUIObjectOf ptr: {}" , pGetUIObjectOf.data ());
324+
325+ /* *
326+ prototype:
327+ HRESULT GetUIObjectOf(
328+ [in] HWND hwndOwner,
329+ [in] UINT cidl,
330+ [in] PCUITEMID_CHILD_ARRAY apidl,
331+ [in] REFIID riid,
332+ [in, out] UINT *rgfReserved,
333+ [out] void **ppv
334+ );
335+
336+ https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellfolder-getuiobjectof
337+ */
338+ static auto GetUIObjectOfHook = pGetUIObjectOf.as_function ().inline_hook ();
339+ GetUIObjectOfHook->install (+[](void *thiz, HWND hwndOwner, UINT cidl,
340+ PCUITEMID_CHILD_ARRAY apidl, REFIID riid,
341+ UINT *rgfReserved, void **ppv) -> HRESULT {
342+ std::println (" GetUIObjectOf called" );
343+ auto res = GetUIObjectOfHook->call_trampoline <HRESULT>(
344+ thiz, hwndOwner, cidl, apidl, riid, rgfReserved, ppv);
345+
346+ // only proceed if requesting IContextMenu
347+ if (riid != IID_IContextMenu || !ppv || !*ppv) {
348+ return res;
349+ }
350+
351+ std::println (" Upgrading context menu" );
352+ IContextMenu *pdcm = (IContextMenu *)(*ppv);
353+ if (SUCCEEDED (res) && pdcm) {
354+ CComPtr<IContextMenu> pCM (pdcm);
355+
356+ HMENU hmenu = CreatePopupMenu ();
357+ pCM->QueryContextMenu (hmenu, 0 , 1 , 0x7FFF ,
358+ CMF_EXPLORE | CMF_CANRENAME);
359+
360+ CComPtr<IContextMenu2> pCM2 = NULL ;
361+ if (SUCCEEDED (pCM->QueryInterface (&pCM2))) {
362+ POINT pt;
363+ GetCursorPos (&pt);
364+ entry::main_window_loop_hook.install (hwndOwner);
365+ block_js_reload.fetch_add (1 );
366+ perf_counter perf (" TrackPopupMenuEx" );
367+ menu menu = menu::construct_with_hmenu (
368+ hmenu, hwndOwner, true ,
369+ [=](int message, WPARAM wParam, LPARAM lParam) {
370+ pCM2->HandleMenuMsg (message, wParam, lParam);
371+ });
372+ perf.end (" construct_with_hmenu" );
373+
374+ auto selected_menu = track_popup_menu (menu, pt.x , pt.y );
375+ mb_shell::context_menu_hooks::block_js_reload.fetch_sub (1 );
376+
377+ if (selected_menu) {
378+ CMINVOKECOMMANDINFOEX ici = {};
379+ ici.cbSize = sizeof (CMINVOKECOMMANDINFOEX);
380+ ici.hwnd = hwndOwner;
381+ ici.fMask = 0x100000 ; /* CMIC_MASK_UNICODE */
382+ ici.lpVerb = MAKEINTRESOURCEA (*selected_menu - 1 );
383+ ici.lpVerbW = MAKEINTRESOURCEW (*selected_menu - 1 );
384+ ici.nShow = SW_SHOWNORMAL;
385+
386+ pCM->InvokeCommand ((LPCMINVOKECOMMANDINFO)&ici);
387+ }
388+
389+ close_next_create_window_exw_window = true ;
390+ }
391+ }
392+
393+ return res;
394+ });
395+ std::println (" GetUIObjectOf hook installed" );
396+ }
0 commit comments