Skip to content

this.count always returns 0 #824

@DoubleThePsycho

Description

@DoubleThePsycho

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions