The best I have is manual RHS holding and Join, after which an arbitrary head could be Applied:
Join @@ Cases[expr, x : _Times :> Hold[x], 3] Hold[2/2, 8/4, 1/0]
This could be done automatically as follows:
makeHeld[(L_ -> R_) | (L_ :> R_)] := L :> HoldComplete[R]; makeHeld[pat_] := x : pat :> HoldComplete[x]; heldCases[expr_, rule_, args___] := Join @@ Cases[Unevaluated @ expr, makeHeld @ rule, args] I am now reasonably happy with this but I still wonder if there is a more elegant or efficient method.
For fun here is a one-line definition of makeHeld using "vanishing patterns""vanishing patterns" and Part propertiesPart properties:
makeHeld[(L_ -> R_) | (L_ :> R_) | L_] := x : L -> HoldComplete[R, x][[{1}]]; It's not quite as efficient and clarity suffers so I won't replace the version above with it.