After some trial and error trying to use this.count for some logical expressions, I turned to DeepWiki for confirmation that this.count was in fact broken. The following message is generated by DeepWiki, but filenames, lines and URLs were not included when copied, they were ,manually copy-pasted here, so something might be incorrect.
I tried to use this.count as part of a title to debug, and it returns 0 no matter how many items are in the menu. Am I being an idiot, or is it really broken?
You're correct - this.count always returning 0 appears to be a bug in the codebase.
The Issue
The this.count property returns context->menu_count FuncExpression.cpp:611-613 , which is a member variable of the Context class initialized to 0 Context.h:166-167 . However, this value is never actually set to a meaningful count during menu item processing.
Evidence
In ContextMenu.cpp, during the prepare_new_items function where menu items are processed and the _this context is set up ContextMenu.cpp:264-516 , there is no code that updates context->menu_count to reflect the actual number of items in the menu.
The MENU class does have a get_count() method that calls the Windows API ::GetMenuItemCount(hMenu) to get the actual item count [MenuItem.h:1264-1268](#4-3) , and the context has an hMenu member Context.h:166-167 , but this is never used to populate menu_count.
Conclusion
This appears to be a missing implementation - context->menu_count is defined but never assigned a value, so it remains at its default of 0. This means this.count cannot be used to determine the number of items in a menu as documented this.html.
Notes
The automatic removal of empty menus in OnInitMenuPopup uses MENU::get_count() directly on the menu handle ContextMenu.cpp:1309-1334 , which works correctly because it calls the Windows API directly. The issue is specifically with the expression system's this.count property not being properly initialized.
Wiki pages you might want to explore:
Citations
Expand
File: src/dll/src/Expression/FuncExpression.cpp (L611-613)
case IDENT_COUNT:
_result = context->menu_count;
break;
File: src/dll/src/Expression/Context.h (L166-167)
HMENU hMenu{};
uint32_t menu_count = 0;
File: src/dll/src/ContextMenu.cpp (L264-516)
bool ContextMenu::prepare_new_items(PositionList &posList,
const std::vector<NativeMenu *> &list,
MenuItemInfo *owner,
menu_t *menu, bool moved)
{
if(list.empty())
return false;
int _this_index = 0;
for(auto item : list)
{
try
{
_context._this = nullptr;
//std::lock_guard<std::mutex> lock(_mutex);
//_context.variables.runtime = &item->owner->variables;
_context.variables.local = &item->owner->variables;
if(item->properties == 0)
{
if(item->is_separator())
{
auto mii = _gc.push(new MenuItemInfo(MIIM_ID | MIIM_FTYPE, MFT_SEPARATOR, -1));
mii->owner = owner;
mii->dynamic = true;
mii->type = NativeMenuType::Separator;
posList.Auto.push_back(mii);
}
continue;
}
auto not_sep = !item->is_separator();
this_item _this{};
_context._this = &_this;
_this.level = (int)parent_level.size();
_this.type = not_sep ? (item->is_menu() ? 2 : 1) : 0;
_this.pos = _this_index++;
if(!Selected.verify_types(item->fso))
continue;
/*
if(Selected.Window.id >= WINDOW_TASKBAR && !Selected.Check(item->fso))
continue;
else if(Selected.Window.id <= WINDOW_TASKBAR && !item->fso.all_types)
{
if(item->fso.Types[FSO_TASKBAR] != Selected.Types[FSO_TASKBAR])
continue;
}*/
if(item->where)
{
if(!_context.eval_bool(item->where))
continue;
}
string value;
if(!item->is_separator() && !moved)
{
if(item->owner != menu->parent)
{
if(_context.Eval(item->moveto, value, true) && !value.trim(L'/').empty())
{
auto mii = _gc.push();
mii->dynamic = true;
mii->owner_dynamic = item;
if(mii->parse_parent(value))
_moved_items.dynamics.push_back(mii);
continue;
}
}
}
auto visibility = _context.parse_visibility(item->visibility);
if(visibility == Visibility::Hidden)
continue;
_this.disabled = visibility == Visibility::Disabled;
_this.vis = static_cast<int>(visibility);
auto privileges = Privileges::None;
auto mode = SelectionMode::Single;
if(owner)
{
mode = owner->mode;
privileges = owner->privileges;
}
mode = _context.parse_mode(item->mode, mode);
if(Selected.Window.id > WINDOW_TASKBAR && !Selected.verify_mode(mode))
continue;
auto position = Position::Auto;
string indexof;
int indexof_pos = 0, indexof_def = -1;
if(item->position)
{
Object obj = _context.Eval(item->position).move();
if(obj.is_array(true))
{
auto ptr = obj.get_pointer();
if((uint32_t)ptr[0] == IDENT_INDEXOF)
{
int ac = ptr[1];
indexof = ptr[2].to_string().move();
indexof_pos = ptr[3];
if(ac == 3)
indexof_def = (int)_context.parse_pos(ptr[4], Position::Auto);
position = (Shell::Position)indexof.trim().hash();
}
}
else if(!obj.is_null())
{
position = _context.parse_pos(obj, Position::Auto);
}
}
// auto position = item->parse_position(&_context);
_this.pos = static_cast<int>(position);
auto push_back = [&](MenuItemInfo *mii)
{
mii->position = position;
mii->dynamic = true;
switch(position)
{
case Position::Top:
posList.Top.push_back(mii);
break;
case Position::Middle:
posList.Middle.push_back(mii);
break;
case Position::Bottom:
posList.Bottom.push_back(mii);
break;
case Position::Auto:
case Position::None:
posList.Auto.push_back(mii);
break;
default:
posList.Custom.push_back(mii);
break;
}
};
if(item->is_separator())
{
auto mii = _gc.push(new MenuItemInfo(MIIM_ID | MIIM_FTYPE, MFT_SEPARATOR, -1));
mii->type = NativeMenuType::Separator;
mii->indexof.val = indexof.move();
mii->indexof.pos = indexof_pos;
mii->indexof.def = indexof_def;
push_back(mii);
}
else
{
string title;
try
{
if(!_context.Eval(item->title, title) || title.empty())
{
/*if(item->is_menu())
is_container = true;
else */if(!item->image.defined)
continue;
}
}
catch(...)
{
}
_this.title = title;
_this.length = title.length<uint32_t>();
FindPattern find;
if(_context.Eval(item->find, value, true) && !value.empty())
{
if(find.split(value, L'|'))
{
auto found = 0;
for(auto sel : Selected.Items)
{
string ext = sel->Extension.substr(1).move();
if(!find(&sel->Title, sel->IsFile() ? &ext : nullptr, &sel->Path))
{
found = 0;
break;
}
found++;
}
if(found == 0) continue;;
}
}
auto mii = _gc.push(new MenuItemInfo(MIIM_STRING | MIIM_ID | MIIM_DATA | MIIM_STATE, 0, ident.get_id()));
mii->owner = owner;
mii->indexof.val = indexof.move();
mii->indexof.pos = indexof_pos;
mii->indexof.def = indexof_def;
mii->set_title(title.move());
mii->id = mii->hash;
mii->hash = mii->hash;
mii->privileges = privileges;
_this.title_normalize = mii->title.normalize;
_this.id = mii->hash;
if(item->is_menu() && item->cmd->admin)
mii->privileges = _context.parse_privileges(item->cmd);
int checked = 0;
if(item->is_item())
{
checked = _context.eval_number<int>(item->checked, 0);
if(_settings.new_items.keys)
_context.Eval(item->keys, mii->keys);
}
if(checked)
{
_this.checked = 1;
mii->fState = MFS_CHECKED;
if(checked == 2)
{
mii->fType |= MFT_RADIOCHECK;
_this.checked = 2;
}
}
mii->separator = (int)_context.parse_separator(item->separator);
mii->column = _context.eval_number<int>(item->column, 0);
mii->type = NativeMenuType::Item;
mii->handle = menu->handle;
mii->owner_dynamic = item;
mii->mode = mode;
mii->visibility = visibility;
File: src/dll/src/ContextMenu.cpp (L1309-1334)
// remove empty dynmic items
for(auto i = items.begin(); i < items.end(); i++)
{
auto item = *i;
if(item->is_popup() && MENU::get_count(item->hSubMenu) == 0)
{
int cp = 0;
for(auto x : __movable_system_items)
cp += x->path.equals((item->path + L"/" + item->title.normalize).trim(L'/'));
for(auto x : _moved_items.dynamics)
cp += item->id == x->parent.front();
if(cp == 0)
{
if(item->dynamic)
{
if(item->owner_dynamic->items.empty())
items.erase(i--);
}
if(item->sys_items && item->sys_items->empty())
items.erase(i--);
}
}
File: src/dll/src/Include/MenuItem.h (L1264-1268)
static int get_count(HMENU hMenu)
{
auto c = ::GetMenuItemCount(hMenu);
return c < 0 ? 0 : c;
}
File: docs/functions/this.html (L8-16)
this.type // Returns the type of the current item [item = 0, menu = 1, separator = 2]
this.checked // Returns true if the current item is checked
this.pos // Returns the position of the current item in the context menu
this.disabled // Returns true if the current item is disabled
this.sys // Returns true if the current item is a system item
this.title // Returns the title of the current item
this.id // Returns the ID of the current item
this.count // Returns the number of items in the context menu
this.length // Returns the length of the current item's title
After some trial and error trying to use
this.countfor some logical expressions, I turned to DeepWiki for confirmation thatthis.countwas in fact broken. The following message is generated by DeepWiki, but filenames, lines and URLs were not included when copied, they were ,manually copy-pasted here, so something might be incorrect.You're correct - this.count always returning 0 appears to be a bug in the codebase.
The Issue
The
this.countproperty returnscontext->menu_countFuncExpression.cpp:611-613 , which is a member variable of theContextclass initialized to 0 Context.h:166-167 . However, this value is never actually set to a meaningful count during menu item processing.Evidence
In
ContextMenu.cpp, during theprepare_new_itemsfunction where menu items are processed and the_thiscontext is set up ContextMenu.cpp:264-516 , there is no code that updatescontext->menu_countto reflect the actual number of items in the menu.The
MENUclass does have aget_count()method that calls the Windows API::GetMenuItemCount(hMenu)to get the actual item count [MenuItem.h:1264-1268](#4-3) , and the context has anhMenumember Context.h:166-167 , but this is never used to populatemenu_count.Conclusion
This appears to be a missing implementation -
context->menu_countis defined but never assigned a value, so it remains at its default of 0. This meansthis.countcannot be used to determine the number of items in a menu as documented this.html.Notes
The automatic removal of empty menus in
OnInitMenuPopupusesMENU::get_count()directly on the menu handle ContextMenu.cpp:1309-1334 , which works correctly because it calls the Windows API directly. The issue is specifically with the expression system'sthis.countproperty not being properly initialized.Wiki pages you might want to explore:
Citations
Expand
File: src/dll/src/Expression/FuncExpression.cpp (L611-613)
File: src/dll/src/Expression/Context.h (L166-167)
File: src/dll/src/ContextMenu.cpp (L264-516)
File: src/dll/src/ContextMenu.cpp (L1309-1334)
File: src/dll/src/Include/MenuItem.h (L1264-1268)
File: docs/functions/this.html (L8-16)