你的浏览器不支持canvas

让我们携手共创奇迹!

Slate问题

Date: Author: LBD

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自WindCrazy

UE的Slate模块

一、使用SWindow时NativeWindow未初始化

遇到的问题

在使用FSlateApplication::Get()SWindow添加到窗口之前(类似调用FSlateApplication::Get().AddWindow(NewWindow);)不能调用使用NativeWindow的函数,因为这个时候NativeWindow还没有初始化只有调用了类AddWindow函数通过MakeWindow调用InSlateWindow->SetNativeWindow(NewWindow);

void FChoseAssetWindow::CreateChooseAssetWindow(const TArray<UObject*>& InObjects, UObject* SelectedItem)
{
    //...
       NewWidget->RefreshSelectableObjects(InObjects);

       FSlateApplication::Get().AddModalWindow(NewWindow, FGlobalTabmanager::Get()->GetRootWindow());
    //...
}
void SChoseAssetWindow::RefreshSelectableObjects(const TArray<UObject*>& InObjects)
{
	if(ParentWindowPtr.IsValid() && !ParentWindowPtr.Pin()->IsVisible()) ParentWindowPtr.Pin()->ShowWindow();
	//...
}
Error        LogOutputDevice           === Handled ensure: ===
Error        LogOutputDevice           Ensure condition failed: NativeWindow.IsValid()  [File:D:\build\++UE5\Sync\Engine\Source\Runtime\SlateCore\Private\Widgets\SWindow.cpp] [Line: 1409] 
Error        LogOutputDevice           Stack: 
Error        LogOutputDevice           [Callstack] 0x00007ffe6c345f9d UnrealEditor-SlateCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe3119f59d UnrealEditor-StandalonTest.dll!SChoseAssetWindow::RefreshSelectableObjects() [D:\UEProjects\ATM\ATM\Plugins\StandalonTest\Source\StandalonTest\Private\ChoseAssetWindow.cpp:313]
Error        LogOutputDevice           [Callstack] 0x00007ffe3118bbc8 UnrealEditor-StandalonTest.dll!FChoseAssetWindow::CreateChooseAssetWindow() [D:\UEProjects\ATM\ATM\Plugins\StandalonTest\Source\StandalonTest\Private\ChoseAssetWindow.cpp:196]
Error        LogOutputDevice           [Callstack] 0x00007ffe311f6a7d UnrealEditor-StandalonTest.dll!StandalonTestUtils::ReplaceReferences() [D:\UEProjects\ATM\ATM\Plugins\StandalonTest\Source\StandalonTest\Private\StandalonTestUtils.cpp:557]
Error        LogOutputDevice           [Callstack] 0x00007ffe311eae97 UnrealEditor-StandalonTest.dll!TBaseRawMethodDelegateInstance<0,FStandalonTestModule,void __cdecl(void),FDefaultDelegateUserPolicy>::ExecuteIfSafe() [D:\UE_5.4\Engine\Source\Runtime\Core\Public\Delegates\DelegateInstancesImpl.h:534]
Error        LogOutputDevice           [Callstack] 0x00007ffe6c80d0f5 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c80d271 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c938ef5 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c90c6aa UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c9aa05c UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c9c8368 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c733975 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c791187 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c77e8f1 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c771aa6 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9cd54f UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9b6f16 UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9cff93 UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9b0e51 UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007fff45ccef5c USER32.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007fff45cce684 USER32.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9d0a96 UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7a780a UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7ce33c UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7ce42a UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7d18a4 UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7e70c4 UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7ea37a UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007fff45997374 KERNEL32.DLL!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007fff46cbcc91 ntdll.dll!UnknownFunction []
Log          LogStats                                 SubmitErrorReport -  0.000 s
Log          LogStats                                     SendNewReport -  1.108 s
Log          LogStats                              FDebug::EnsureFailed -  1.163 s
Warning      LogOutputDevice           Script Stack (0 frames) :
Log          LogStats                  FPlatformStackWalk::StackWalkAndDump -  0.026 s
Error        LogOutputDevice           === Handled ensure: ===
Error        LogOutputDevice           Ensure condition failed: FoundViewInfo  [File:D:\build\++UE5\Sync\Engine\Source\Runtime\SlateRHIRenderer\Private\SlateRHIRenderer.cpp] [Line: 1721] 
Error        LogOutputDevice           Stack: 
Error        LogOutputDevice           [Callstack] 0x00007ffe5d75bc33 UnrealEditor-SlateRHIRenderer.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c77859a UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c75a061 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c799583 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c74f3e4 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe3118bdbd UnrealEditor-StandalonTest.dll!FChoseAssetWindow::CreateChooseAssetWindow() [D:\UEProjects\ATM\ATM\Plugins\StandalonTest\Source\StandalonTest\Private\ChoseAssetWindow.cpp:201]
Error        LogOutputDevice           [Callstack] 0x00007ffe311f6a7d UnrealEditor-StandalonTest.dll!StandalonTestUtils::ReplaceReferences() [D:\UEProjects\ATM\ATM\Plugins\StandalonTest\Source\StandalonTest\Private\StandalonTestUtils.cpp:557]
Error        LogOutputDevice           [Callstack] 0x00007ffe311eae97 UnrealEditor-StandalonTest.dll!TBaseRawMethodDelegateInstance<0,FStandalonTestModule,void __cdecl(void),FDefaultDelegateUserPolicy>::ExecuteIfSafe() [D:\UE_5.4\Engine\Source\Runtime\Core\Public\Delegates\DelegateInstancesImpl.h:534]
Error        LogOutputDevice           [Callstack] 0x00007ffe6c80d0f5 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c80d271 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c938ef5 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c90c6aa UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c9aa05c UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c9c8368 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c733975 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c791187 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c77e8f1 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffe6c771aa6 UnrealEditor-Slate.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9cd54f UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9b6f16 UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9cff93 UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9b0e51 UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007fff45ccef5c USER32.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007fff45cce684 USER32.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ffeff9d0a96 UnrealEditor-ApplicationCore.dll!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7a780a UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7ce33c UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7ce42a UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7d18a4 UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7e70c4 UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007ff6cc7ea37a UnrealEditor.exe!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007fff45997374 KERNEL32.DLL!UnknownFunction []
Error        LogOutputDevice           [Callstack] 0x00007fff46cbcc91 ntdll.dll!UnknownFunction []
Log          LogStats                                 SubmitErrorReport -  0.000 s
	The ViewportInfo could not be found for Window.
Error        LogSlate                  The ViewportInfo could not be found for Window.

二、使用TSharedPtr管理Slate造成多次释放同一内存

#pragma once

DECLARE_LOG_CATEGORY_EXTERN(LogChoseAssetWindow, Log, Log);

class SChoseAssetWindow;

class FChoseAssetWindow
{
public:
    static void CreateChooseAssetWindow( const TArray<UObject*>& InObjects, UObject* SelectedItem = nullptr );

    static void GetChoseResult(UObject*& Result, TArray<UObject*>& OutNotSelected, bool& bIsBreak);
private:
    static TSharedPtr<SChoseAssetWindow> WidgetInstance;
};
#include "ChoseAssetWindow.h"

DEFINE_LOG_CATEGORY(LogChoseAssetWindow)
#define LOCTEXT_NAMESPACE "LogChoseAssetWindow"

class SChoseAssetWindow : public SBorder
{
public:
    SLATE_BEGIN_ARGS(SChoseAssetWindow)
       : _ParentWindow()
    {}
    SLATE_ARGUMENT(TSharedPtr<SWindow>, ParentWindow)
SLATE_END_ARGS()

private:
    TWeakPtr<SWindow> ParentWindowPtr;
};

TSharedPtr<SChoseAssetWindow> FChoseAssetWindow::WidgetInstance;

void FChoseAssetWindow::CreateChooseAssetWindow(const TArray<UObject*>& InObjects, UObject* SelectedItem)
{
       TSharedRef<SWindow> NewWindow = SNew(SWindow)
       .Title(LOCTEXT("Choose_Title", "Replace References"))
       .ClientSize( FVector2D(768,300) );

       TSharedPtr<SChoseAssetWindow> NewWidget;
       if(WidgetInstance.IsValid())
       {
          NewWidget = MoveTemp(WidgetInstance);
       }else
       {
          SAssignNew(NewWidget, SChoseAssetWindow)
          .ParentWindow(NewWindow);
       }
       
       NewWindow->SetContent(NewWidget.ToSharedRef());

       FSlateApplication::Get().AddModalWindow(NewWindow, FGlobalTabmanager::Get()->GetRootWindow());

       WidgetInstance = NewWidget;
}

void FChoseAssetWindow::GetChoseResult(UObject*& Result, TArray<UObject*>& OutNotSelected, bool& bIsBreak)
{
    Result = nullptr;
    OutNotSelected.Empty();
    if(WidgetInstance.IsValid())
    {
       WidgetInstance->GetChoseObjects(Result, OutNotSelected);
       bIsBreak = WidgetInstance->GetIsCancelAll();
       WidgetInstance->ClearSelectableObjects();
    }
}

问题在于静态智能指针 WidgetInstance 的生命周期管理和引用计数处理不当,导致对象被多次释放。

问题原因分析

  1. 静态指针的生命周期问题
    • static TSharedPtr<SChoseAssetWindow> WidgetInstance 是静态变量,其生命周期持续到程序结束
    • 当程序退出时,静态对象会按不确定的顺序析构(可能早于Slate系统)
  2. 双重所有权问题
    if(WidgetInstance.IsValid()) {
        NewWidget = MoveTemp(WidgetInstance);  // 转移所有权到局部变量
        // ...
    }
    // ...
    WidgetInstance = NewWidget;  // 所有权移回静态变量
    
    • 当窗口关闭时,Slate系统会尝试释放窗口内容(NewWidget
    • 但静态指针 WidgetInstance 仍然持有该对象的引用
    • 程序结束时静态指针析构时再次尝试释放已释放对象

解决方案

推荐方案:移除静态实例重用(推荐)

直接每次创建新实例,避免静态指针:

void FChoseAssetWindow::CreateChooseAssetWindow(const TArray<UObject*>& InObjects, UObject* SelectedItem)
{
    TSharedRef<SWindow> NewWindow = SNew(SWindow)
        .Title(LOCTEXT("Choose_Title", "Replace References"))
        .ClientSize(FVector2D(768, 300));
    
    // 始终创建新实例
    TSharedRef<SChoseAssetWindow> NewWidget = SNew(SChoseAssetWindow)
        .ParentWindow(NewWindow)
        .Note(LOCTEXT("Asset_Select", "Select an asset."));
    
    NewWindow->SetContent(NewWidget);
    NewWidget->RefreshSelectableObjects(InObjects);
    
    if (SelectedItem) {
        NewWidget->SetSelectedItem(SelectedItem);
    }
    
    FSlateApplication::Get().AddModalWindow(NewWindow, FGlobalTabmanager::Get()->GetRootWindow());
}

替代方案:正确管理静态实例(若必须重用)

如果必须重用实例,需确保:

  1. 在窗口关闭时重置静态指针
  2. 正确处理所有权
// 修改后的实现
void FChoseAssetWindow::CreateChooseAssetWindow(...)
{
    ...
    TSharedPtr<SChoseAssetWindow> NewWidget;
    
    if (WidgetInstance.IsValid() && WidgetInstance->GetParentWindow().IsValid())
    {
        NewWidget = WidgetInstance;
    }
    else
    {
        SAssignNew(NewWidget, SChoseAssetWindow)
            .ParentWindow(NewWindow)
            .Note(...);
        
        // 监听窗口关闭事件
        NewWindow->SetOnWindowClosed(FOnWindowClosed::CreateLambda(
            [](const TSharedRef<SWindow>&) {
                WidgetInstance.Reset();  // 窗口关闭时重置静态指针
            }));
    }
    
    ... // 其余逻辑
    
    WidgetInstance = NewWidget;
}

崩溃原理示意图

程序启动
  |
  ├─ 创建 WindowInstance (ref=1)
  ├─ 窗口关闭:Slate释放对象 (ref=0) → 实际删除
  └─ 程序结束:静态变量析构 → 再次尝试释放 → 访问野指针 → 崩溃

建议采用每次创建新窗口的方案,这是最安全且符合Slate设计理念的做法。静态实例重用往往带来复杂的生命周期问题,除非有严格的性能需求,否则应避免。


对于本文内容有问题或建议的小伙伴,欢迎提出问题