I use the same call to CreateWidget() in two classes, one works, one doesn't, how is that possible?

Hi everyone,

I’m trying to instance some blueprints from a C++ class. I got it working in two cases but my third case fails. I get an Exception 0x80000003 encountered at address inside of CreateWidget in the line return Cast(UUserWidget::CreateWidgetInstance(*OwningObject, UserWidgetClass, WidgetName));. Here’s an overview of my code.

// In UQuestRadioMatrixQuestion.h:
TArray<TObjectPtr<UQuestRadioMatrixRow>> rows;

// In UQuestRadioMatrixQuestion.cpp:
void UQuestRadioMatrixQuestion::NativeConstruct() {
    // ...
    rows[i] = NewObject<UQuestRadioMatrixRow>();
    rows[i]->Setup();
    // ...

// In UQuestRadioMatrixRow.cpp:
void UQuestRadioMatrixRow::Setup() {
    // ...
	TObjectPtr<UQuestCheckbox> checkBox(CreateWidget<UQuestCheckbox>(this, CheckboxBPClass)); // This is what calls CreateWidget<>() which then throws an exception. It's not this line that fails, it's the one in CreateWidget<>() I mentioned above.
	// ...
}

// UQuestRadioMatrixRow.h:
UCLASS()
class UNREALQUESTBP56_API UQuestRadioMatrixRow : public UQuestRadioQuestion
{
	GENERATED_BODY()
	
public:
	virtual void Setup() override;
    // ...
};

// UQuestRadioQuestion.h:
UCLASS()
class UNREALQUESTBP56_API UQuestRadioQuestion : public UQuestCheckboxQuestion {
	GENERATED_BODY()

public:
// ...
};

// UQuestCheckboxQuestion.h:
UCLASS()
class UNREALQUESTBP56_API UQuestCheckboxQuestion : public UQuestQuestion {
	GENERATED_BODY()

protected:
	virtual void NativePreConstruct() override;

public:
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
	TSubclassOf<UQuestCheckbox> CheckboxBPClass;

	TArray<TObjectPtr<UQuestCheckbox>> AnswerBoxes;

	UFUNCTION(BlueprintCallable)
	virtual void Setup();
};

// UQuestQuestion ultimately inherits from UUserWidget

I obviously left out all the members that are not involved in the problematic function call.

Now you can see that there is another version of Setup() in UQuestCheckboxQuestion. It looks like this:

void UQuestCheckboxQuestion::Setup() {
	TObjectPtr<UQuestCheckbox> checkBox(CreateWidget<UQuestCheckbox>(this, CheckboxBPClass));
    // ...
	}
}

You can see the line is identical to the one in the other Setup function. All the parts that differ are unrelated to the call to CreateWidget<>(). I used the debugger and checked and the only difference in the call is the content of this. I also made sure that CheckboxBPClass is set in all cases. It is.

Now I can call Setup() on instances of both UQuestCheckboxQuestion and UQuestRadioQuestion and they will create widgets just fine, but on UQuestRadioMatrixRow it doesn’t work and I am puzzled by this, since UQuestRadioMatrixRow inherits from the other ones.

There is one other thing that’s different that I can think of. As you can see, instances of UQuestRadioMatrixRow are created with NewObject(). Instances of the other two classes however are created from blueprints (Create Widget node), since there are blueprint classes that inherit from UQuestCheckboxQuestion and UQuestRadioQuestion. I can’t see how that would explain my problem though.

Please help me fix this issue and understand what’s going on.

The array may also be part of the problem: if rows was not resized with SetNum() before doing rows[i] = ..., then you are writing out of bounds. If the array is empty, you should either use rows.Add(...) or call rows.SetNum(Count) before indexing it. Otherwise, that invalid write can corrupt memory and cause a crash later, including around CreateWidget().

According to ChatGPT, the problem may be that UQuestRadioMatrixRow is being created with NewObject() even though it is a UUserWidget-derived class. Because of that, this may not be a valid widget owner or world context when you later call CreateWidget<UQuestCheckbox>(this, CheckboxBPClass), which could explain why the crash happens inside CreateWidget(). That said, ChatGPT often hallucinates and can be completely wrong, so this should be treated only as a possible explanation, not a confirmed cause.

I used SetNum(). If I was writing out of bounds, I would not be able to even call Setup().

I also came up with the idea that NewObject() might be related, however, that seems to be the correct way to instantiate a class like that and I don’t know of any other way.

Since the content of this is the only difference, I continued to experiment myself and instead of using this, which would be an instance of UQuestRadioMatrixRow, I created a new member in UQuestRadioMatrixRow, set it to the calling instance of UQuestRadioMatrixQuestion and used that as the owning type in the CreateWidget<>() call. It worked!

UQuestRadioMatrixQuestion, like UQuestCheckboxQuestion and UQuestRadioQuestion, is not instanced directly from C++ code, instead, an instance of a blueprint class that derives from it is created from another blueprint. So, it looks like CreateWidget<>() not only requires the owning type to be derived from UUserWidget, it even needs it to be a blueprint type derived from UUserWidget.