There's an old Windows 3.1 app I'm trying to use under wine, but it's menubar is missing. It was used for in house development for a really old game, but the programmer has asked me not to redistribute it so I can't provide a link/download. The application otherwise looks and acts exactly as it should -- but the File/About/etc. menubar is missing and thus the ability to use any of those options (I don't think the app has any accelerators).
Seeing as this is such a "simple" bug (watch me eat my words) I thought it'd be a good chance to try some wine hacking. So I read the wine developer guide and tried my best. But my unfamiliarity with the Windows API / Wine has me stuck :P
I've included the results of me running with WINEDEBUG=+menu below. The only thing that sticks out to me is this:
warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0
Which occurs multiple times. Looking at dlls/user/menu.c (function starts on line 101), it looks like USER_HEAP_LIN_ADDR is returning a 0 pointer which causes the warning. Someone in #winehq said USER_HEAP_LIN_ADDR is for 16<->32 bit conversion because 16bit apps use segment:offset to address memory (I'm still not totally sure how a segment differs from a memory address) while 32bit apps use flat addresses. USER_HEAP_LIN_ADDR itself looks like a very magical macro where to look based on it.
strings informs me it's a Borland C++ 1991 app, if that's important.
When I did a +relay all that showed up between the warn:menu's was calls to ENABLEMENUITEM.
Hints for a newb? :)
trace:menu:CreateMenu return 0x50 trace:menu:MENU_GetSysMenu hWnd 0x10022 (hMenu 0x50) trace:menu:LoadMenuIndirectW 0x7f9cea54, ver 0 trace:menu:CreateMenu return 0x94 trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id f120, str L"&Restore" trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=0x7f9cea5c trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0xf120, Text=L"&Restore" } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id f010, str L"&Move" trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=0x7f9cea72 trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0xf010, Text=L"&Move" } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id f000, str L"&Size" trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=0x7f9cea82 trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0xf000, Text=L"&Size" } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id f020, str L"Mi&nimize" trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=0x7f9cea92 trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0xf020, Text=L"Mi&nimize" } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id f030, str L"Ma&ximize" trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=0x7f9ceaaa trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0xf030, Text=L"Ma&ximize" } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id 0000, str (nil) (not a string) trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=(nil) trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0x0, Type=sep } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id f060, str L"&Close\tAlt-F4" trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=0x7f9ceac8 trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0xf060, Text=L"&Close\tAlt-F4" } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id 0000, str (nil) (not a string) trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=(nil) trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0x0, Type=sep } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id f130, str L"&Switch to ...\tCtrl-Esc" trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=0x7f9ceaee trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0xf130, Text=L"&Switch to ...\tCtrl-Esc" } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id 0000, str (nil) (not a string) trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=(nil) trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0x0, Type=sep } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id f141, str L"&About WINE ..." trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=0x7f9ceb28 trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0xf141, Text=L"&About WINE ..." } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000400, id 0000, str (nil) (not a string) trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=400 str=(nil) trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0x0, Type=sep } trace:menu:InsertMenuW hMenu 0x94, pos -1, flags 00000480, id f142, str L"&Put 'Debug mark' in debug log" trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=480 str=0x7f9ceb52 trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0xf142, Text=L"&Put 'Debug mark' in debug log" } trace:menu:SetMenuDefaultItem (0x94,61536,0) trace:menu:MENU_CopySysPopup returning 0x94. trace:menu:InsertMenuW hMenu 0x50, pos -1, flags 00002410, id 0094, str (nil) (not a string) trace:menu:do_debug_print_menuitem MENU_SetItemData from: { ID=0x0, Text=Null } trace:menu:MENU_SetItemData flags=2410 str=(nil) trace:menu:do_debug_print_menuitem MENU_SetItemData to : { ID=0x94, Sub=0x94, Type=sep,pop,rorder } trace:menu:MENU_GetSysMenu hMenu=0x50 (hPopup 0x94) trace:menu:GetMenu for 0x10022 returning (nil) trace:menu:GetMenu for 0x10022 returning (nil) trace:menu:GetMenu for 0x10022 returning (nil) trace:menu:EnableMenuItem ((nil), 00ca, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00cb, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00c9, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00cf, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00d1, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00cc, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00cd, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00ce, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00d0, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 012e, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 012d, 0001) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:GetMenu for 0x10026 returning (nil) trace:menu:GetMenu for 0x10026 returning (nil) trace:menu:GetMenu for 0x10022 returning (nil) trace:menu:GetMenu for 0x10022 returning (nil) trace:menu:GetMenu for 0x10022 returning (nil) trace:menu:EnableMenuItem ((nil), 00ca, 0000) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00cb, 0000) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00c9, 0000) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00cf, 0000) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00d1, 0000) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00cc, 0000) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00cd, 0000) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00ce, 0000) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:EnableMenuItem ((nil), 00d0, 0000) ! warn:menu:MENU_GetMenu invalid menu handle=(nil), ptr=(nil), magic=0 trace:menu:GetMenuState (menu=0x94, id=f060, flags=0000); trace:menu:do_debug_print_menuitem item: { ID=0xf060, State=default, Text=L"&Close\tAlt-F4" } trace:menu:GetMenuState (menu=0x94, id=f060, flags=0000); trace:menu:do_debug_print_menuitem item: { ID=0xf060, State=default, Text=L"&Close\tAlt-F4" } trace:menu:DestroyMenu (0x50) trace:menu:DestroyMenu (0x94)
So I've worked on this some more but I'm still stuck =)
The +menu log shows a couple CreateMenu calls that I can immediately dismiss -- they're for the system menu (the one you get when you click the upper left box in the title bar). Using +win, there's a call to CreateWindowEx that passes a value for hMenu, but the name of this window is "listbox" and the app does have a listbox, and the listbox doesn't have it's own menu, so I'm assuming as per the MSDN docs this hMenu is actually a child window identifier (MSDN says that can be the case for certain window styles, and it looks like dlls/user/win.c does check for this, see here: http://tinyurl.com/qp1q).
Question: What's the "correct" behavior for CreateWindowEx to take if no hMenu is passed in english? I'm not sure what logic the code snippet below is following.
There's another call to CreateWindowEx that appears to be making the main window, that has no hMenu value passed to it. I planted a bunch of TRACE's in dlls/user/win.c and found that it's getting to the following section of CreateWindowEx:
(Around line 1046 or so, should be 10 or so lines off because of stuff I've added)
LPCSTR menuName = (LPCSTR)GetClassLongPtrA( hwnd, GCLP_MENUNAME ); if (menuName) { if (!cs->hInstance || HIWORD(cs->hInstance)) { TRACE("We get right here\n"); cs->hMenu = LoadMenuA(cs->hInstance,menuName); } else { cs->hMenu = HMENU_32(LoadMenu16(HINSTANCE_16(cs->hInstance),menuName)); }
if (cs->hMenu) { MENU_SetMenu( hwnd, cs->hMenu ); } }
In this section menuName is the same as the class name passed to CreateWindowEx. I don't know if that should cause problems. I haven't started looking at what GetClassLongPtrA does yet.
LoadMenuA must end up returning null, because it never ends up getting to the SetMenu call. Through judicious trace planting, I've deduced this string of calling:
LoadMenuA - > FindResourceA -> FindResourceExA -> findresourceA -> LdrFindResource_U -> find_entry -> RtlImageDirectoryEntryToData
And then a check this check returns true:
(Around line 2037 :P)
if (!(addr = nt->OptionalHeader.DataDirectory[dir].VirtualAddress)) return NULL;
At that point I stopped because I've now delved into the realm of deep black magic, and I suspect the bug must be somewhere earlier in the chain (probably in CreateWindowEx).
Thoughts? Ideas? :P