Possible to check if Event Dispatcher has events bound?

I needed to see what was bound to an event, and this was one of the threads that helped me figure it out, along with Getting a list of events from blueprint and calling them in C++

It is possible to create a blueprint library function in C++, that helps you get this information, through Unreal’s reflection system.

It looks like events created through the unreal editor, are of type FMulticastScriptDelegate (at least that’s how ScriptCore.cpp seems to call the events). But you can use the TFieldIterator with FMulticastDelegateProperty to get a list of the events on an object.

The blogpost on Reflection in Unreal( Unreal Property System (Reflection) - Unreal Engine ) only mentions UProperty, and things like UENUM, UCLASS, USTRUCT, and UFUNCTION, but you can also use subclasses to filter, apparently. The fact that UProperty was refactored to FProperty makes things extra confusing, but FMulticastDelegateProperty is valid, so yay.

header file:

UFUNCTION( BlueprintCallable, Category = "Event" )
static TArray<FString> GetEventNames( UObject* Target);

implementation file:

TArray<FString> UEventInfo::GetEventNames( UObject* Target )
{
	UClass* classReference = Target->GetClass();
	TArray<FString> eventNames;

	for (TFieldIterator<FMulticastDelegateProperty> propertyIterator( classReference, EFieldIteratorFlags::ExcludeSuper ); propertyIterator; ++propertyIterator)
	{
		FMulticastDelegateProperty* Property = *propertyIterator;
		eventNames.Add( propertyIterator->GetName() );
	}
		
	return eventNames;
}

In that example I’m excluding inherited events, but you can just remove that flag to get all of them.

To get a list of the bound objects, you need a reference to a specific instance of the event, rather than a class wide property. ScriptCore.cpp mentioned above has a call to GetMulticastDelegate, but that requires an address, and it appears that you can’t just use a reference to the UObject. The thread Correct way to get the value of a Property? points in the right direction, by calling ContainerPtrToValuePtr first.

That lets you get the reference to the MulticastScriptDelegate, which lets you call GetAllObjects().
Header:

UFUNCTION( BlueprintCallable, Category = "Event" )
static TArray<UObject*> GetObjectsReferencedByEvent( UObject* Target, FString EventName );
TArray<UObject*> UEventInfo::GetObjectsReferencedByEvent( UObject* Target, FString EventName )
{
	TArray<UObject*> referencedObjects;
	UClass* classReference = Target->GetClass();

	for (TFieldIterator<FMulticastDelegateProperty> propertyIterator( classReference, EFieldIteratorFlags::ExcludeSuper ); propertyIterator; ++propertyIterator)
	{
		FMulticastDelegateProperty* Property = *propertyIterator;
		if (propertyIterator->GetName() == EventName) {
			void* ValueAddress = Property->ContainerPtrToValuePtr< void >( Target );
			const FMulticastScriptDelegate* DelegateAddr = Property->GetMulticastDelegate( ValueAddress );

			referencedObjects.Append( DelegateAddr->GetAllObjects());
		}
	}

	return referencedObjects;
}

GetAllObjects says:

For advanced uses only – you should never need call this function in normal circumstances.

but I’d argue that in normal circumstances, the list of objects referenced by a blueprint event should already exist, because how else are you going to precisely debug whether events are correctly subscribed to or unsubscribed from. So when it doesn’t exist, we have little choice but calling GetAllObjects ourselves.

Anyway, I hope that helps someone. (It might at least help me in the future)

1 Like