C++ crash on exit (CryEngine, Ultralight)

I'm coding a game in CryEngine and just finished implementing HTML renderer called Ultralight to replace the built in Scaleform UI. Everything works as expected except for one specific case. The program crashes in exe_common.inl at
1
2
if (!__scrt_is_managed_app())
            exit(main_result);

with Exception thrown at 0x00007FF88F71028C in GameLauncher.exe: 0xC0000005: Access violation executing location 0x00007FF88F71028C.
That only happens if I have created an instance of UIManager but haven't created a view in this function
1
2
3
4
5
6
7
8
9
void CUIManager::CreateView(const char* name, const char* file, bool transparent, bool listenToEvents)
{
	ultralight::Ref<ultralight::View> view = m_pRenderer->CreateView(gEnv->pRenderer->GetWidth(), gEnv->pRenderer->GetHeight(), transparent);
	view->LoadURL(ultralight::String("file:///") + ultralight::String(file));
	m_views.push_back(SRenderView(name, view, transparent));
	
	if (listenToEvents)
		m_inputListener.AddView(view);
}

where m_views is a DynArray<SRenderView>. DynArray is basically just an std::vector in CryEngine and the same crash happens with std::vector as well.


It doesn't matter if I remove the view I created and then exit, the program doesn't crash. Also, if I never create an instance of the UIManager the program doesn't crash.

Some other functions and definitions that might help
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// in RenderView.h
struct SRenderView
{
	SRenderView() : name("invalid"), texture(nullptr), view(nullptr), bHidden(false), bTransparent(false)
	{
	}

	SRenderView(string name, ultralight::RefPtr<ultralight::View> view, bool transparent) : name(name), view(view), bTransparent(bTransparent), bHidden(false), texture(nullptr)
	{
	}

	~SRenderView()
	{
		if (texture)
		{
			texture->Release();
			texture = nullptr;
		}
	}

	void CheckUpdate()
	{
		if (!view || bHidden)
			return;

		if (view->is_bitmap_dirty())
		{
			ultralight::RefPtr<ultralight::Bitmap> bitmap = view->bitmap();

			// just create the texture once
			if (!texture)
			{
				texture = gEnv->pRenderer->CreateTexture(string().Format("ul-%s", name), bitmap->width(), bitmap->height(), 1, (uint8*)bitmap->raw_pixels(), eTF_R8G8B8A8, FT_NOMIPS | FT_USAGE_TEMPORARY);
				desc.color = ColorB(255, 255, 255, 255);
				desc.stereoDepth = 0;
				desc.uv[0] = Vec2(0, 1);
				desc.uv[1] = Vec2(1, 0);
				desc.textureId = texture->GetTextureID();
			}
			else
			{
				gEnv->pRenderer->UpdateTextureInVideoMemory(texture->GetTextureID(), (uint8*)bitmap->raw_pixels(), 0, 0, bitmap->width(), bitmap->height(), eTF_R8G8B8A8);
			}

			if (!texture)
				return;

			desc.w = (float)bitmap->width();
			desc.h = (float)bitmap->height();
			desc.renderFlags = e_Mode2D | e_DepthTestOff | (bTransparent ? e_AlphaBlended : e_AlphaNone) | e_CullModeNone;
		}
	}

	void Render()
	{
		if (!texture || bHidden)
			return;

		gEnv->pRenderer->GetIRenderAuxGeom()->PushImage(desc);
	}

	string name;
	ultralight::RefPtr<ultralight::View> view;
	ITexture* texture;
	SRender2DImageDescription desc;
	bool bTransparent;
	bool bHidden;
};
// ~RenderView.h

// UIManager.cpp
CUIManager::CUIManager() : m_bIsInitialized(false), m_fileSystem(""), m_pRenderer(ultralight::Renderer::Create())
{
}

CUIManager::~CUIManager()
{
	ultralight::Platform& platform = ultralight::Platform::instance();
	platform.set_file_system(nullptr);
	platform.set_font_loader(nullptr);
}

void CUIManager::Init()
{
	ultralight::Config config;
	config.face_winding = ultralight::kFaceWinding_Clockwise;
	config.device_scale_hint = 1.0;
	config.font_family_standard = "Arial";

	ultralight::Platform& platform = ultralight::Platform::instance();
	platform.set_config(config);
	platform.set_file_system(&m_fileSystem);
	platform.set_font_loader(&m_fontLoader);

	m_inputListener.Init();

	m_bIsInitialized = true;
}

void CUIManager::Update()
{
	if (!m_bIsInitialized)
		return;

	m_pRenderer->Update();
	m_pRenderer->Render();

	for (SRenderView& v : m_views)
	{
		v.CheckUpdate();
		v.Render();
	}
}
// ~UIManager.cpp 
What's in the stack when the program crashes?
1
2
3
4
5
6
7
8
9
10
11
 	00007ff8936a028c()	Unknown
	ntdll.dll!RtlProcessFlsData()	Unknown
 	ntdll.dll!LdrShutdownProcess()	Unknown
 	ntdll.dll!RtlExitUserProcess()	Unknown
 	kernel32.dll!ExitProcessImplementation()	Unknown
 	ucrtbase.dll!exit_or_terminate_process()	Unknown
 	ucrtbase.dll!common_exit()	Unknown
> 	GameLauncher.exe!__scrt_common_main_seh() Line 295	C++
 	kernel32.dll!BaseThreadInitThunk()	Unknown
 	ntdll.dll!RtlUserThreadStart()	Unknown


GameLauncher.exe!__scrt_common_main_seh() Line 295 C++
is the exit call in exe_common.inl

EDIT: made the stack more readable
Last edited on
Weird. It looks as if someone held a pointer to a function in a DLL that was unloaded.
I think you're spot on. I threw a dump into WinDbg and if I understand correctly it seems like it's trying to call some function in WebCore.dll after it has been unloaded.
1
2
3
4
5
6
7
8
9
10
<Unloaded_WebCore.dll>+0x100028c
ntdll!RtlProcessFlsData+0x145
ntdll!LdrShutdownProcess+0x92
ntdll!RtlExitUserProcess+0xad
kernel32!ExitProcessImplementation+0xa
ucrtbase!exit_or_terminate_process+0x44
ucrtbase!common_exit+0x6f
GameLauncher!__scrt_common_main_seh+0x168
kernel32!BaseThreadInitThunk+0x14
ntdll!RtlUserThreadStart+0x21
Last edited on
Topic archived. No new replies allowed.