Die saubere Lösung verwendet Jobs. Jobs sind Sandkasten für Prozesse. Wenn ein Prozess in einem Job ist, sind seine Kindprozesse auch in dem Job (sofern der Besitzer des Jobs und der Prozess nicht beide sagen, dass dem nicht so sein soll). Du kannst dann die Anzahl aktiver Prozesse im Job abfragen. Wenn sie Null ist, kehrst du zurück. Für einen ereignisgesteuerten Ansatz kann man dem Job einen IO Completion Port zuweisen, welcher eine Nachricht erhält, wenn alle Prozesse beendet sind. Das muss allerdings in einem eigenen Thread geschehen, da es dann nicht möglich ist, auf Fensternachrichten zu reagieren.
Deine bisherige Lösung ist übrigens nicht optimal, da durch die Schleife unnötig CPU-Zeit verbraucht wird. Korrekt wäre es, mit MsgWaitForMultipleObjects auf das
Handle zu warten.