raylib-go icon indicating copy to clipboard operation
raylib-go copied to clipboard

Raygui -Access violation crash in GuiTextBox on Windows - Exception 0xc0000005

Open janicaleksander opened this issue 5 months ago • 4 comments

Application crashes with access violation when calling gui.TextBox() from raylib-go/raygui on Windows.

Exception 0xc0000005 0x0 0xc001b94000 0x7bed93 PC=0x7bed93 signal arrived during external code execution

github.com/gen2brain/raylib-go/raygui._Cfunc_GuiTextBox({0x44070000, 0x43b40000, 0x43480000, 0x42700000}, 0xc001b93ff8, 0x40, 0x0) _cgo_gotypes.go:835 +0x50 github.com/gen2brain/raylib-go/raygui.TextBox({0x0?, 0x0?, 0xecd400?, 0x0?}, 0xc0001feab0, 0x40, 0x0) C:/Users/Aleksander/go/pkg/mod/github.com/gen2brain/raylib-go/[email protected]/raygui.go:980 +0x158

gui.TextBox(bounds, &textString, 64, true)

Issue: The crash occurs consistently when calling gui.TextBox(). This appears to be a CGO-related issue where the Go garbage collector may be moving string memory while C code is accessing it, causing an invalid memory access.

Expected Behavior: TextBox should render without crashing.

Actual Behavior:
Application terminates with access violation exception.

janicaleksander avatar Jul 29 '25 20:07 janicaleksander

Hey @janicaleksander ,

can you please share a code snippet or example to reproduce the error?

JupiterRider avatar Jul 30 '25 17:07 JupiterRider

w.createUserScene.emailInput = InputField{ bounds: rl.NewRectangle( float32(w.width/2)-100, float32(w.height/2-300), 200, 60, ), text: "", focus: false, textSize: 64, }

gui.TextBox(w.createUserScene.emailInput.bounds, &w.createUserScene.emailInput.text, w.createUserScene.emailInput.textSize, w.createUserScene.emailInput.focus)

This is the tricky part, because this error occurred twice in a row, and then I tried to do it again and this time it didn't happen.

janicaleksander avatar Jul 30 '25 18:07 janicaleksander

@janicaleksander, Occasional crashes suggest you are doing more in your code than what is shown. You are not using Go routines, are you? If not, as you mentioned, it's possible that GC is cleaning your objects, but from the C side, they're still needed. You can test that by simply adding runtime.KeepAlive(yourobject) at the end, or after the part where it is crashing, to force GC not to clean it. Edit: Or just try with GOGC=off.

gen2brain avatar Jul 31 '25 06:07 gen2brain

package Application

import (
	gui "github.com/gen2brain/raylib-go/raygui"
	rl "github.com/gen2brain/raylib-go/raylib"
	"github.com/janicaleksander/bcs/Proto"
	"github.com/janicaleksander/bcs/User"
	"strconv"
)

type CreateUserScene struct {
	emailInput      InputField
	passwordInput   InputField
	rePasswordInput InputField
	ruleLevelInput  InputField
	nameInput       InputField
	surnameInput    InputField
	isError         bool
	errorMessage    string
	acceptButton    Button

	isAccept      bool
	acceptMessage string
	//maybe sobe checkbox
}

func (s *CreateUserScene) Reset() {
	s.isError = false
	s.errorMessage = ""

	s.isAccept = false
	s.acceptMessage = ""
}

func (w *Window) createUserSceneSetup() {
	w.createUserScene.Reset()
	w.createUserScene.emailInput = InputField{
		bounds: rl.NewRectangle(
			float32(w.width/2)-100,
			float32(w.height/2-300),
			200,
			60,
		),
		text:     "",
		focus:    false,
		textSize: 64,
	}
	w.createUserScene.passwordInput = InputField{
		bounds: rl.NewRectangle(
			float32(w.width/2)-100,
			float32(w.height/2)-200,
			200,
			60),
		text:     "",
		focus:    false,
		textSize: 64,
	}
	w.createUserScene.rePasswordInput = InputField{
		bounds: rl.NewRectangle(
			float32(w.width/2)-100,
			float32(w.height/2)-100,
			200,
			60),
		text:     "",
		focus:    false,
		textSize: 64,
	}
	w.createUserScene.ruleLevelInput = InputField{
		bounds: rl.NewRectangle(
			float32(w.width/2)-100,
			float32(w.height/2),
			200,
			60,
		),
		text:     "",
		focus:    false,
		textSize: 64,
	}
	w.createUserScene.nameInput = InputField{
		bounds: rl.NewRectangle(
			float32(w.width/2)-100,
			float32(w.height/2)+100,
			200,
			60),
		text:     "",
		focus:    false,
		textSize: 64,
	}
	w.createUserScene.surnameInput = InputField{
		bounds: rl.NewRectangle(
			float32(w.width/2)-100,
			float32(w.height/2)+200,
			200,
			60,
		),
		text:     "",
		focus:    false,
		textSize: 64,
	}
	w.createUserScene.acceptButton = Button{
		bounds: rl.NewRectangle(
			float32(w.width/2)-100,
			float32(w.height/2+280),
			200,
			60),
		text: "ACCEPT",
	}
}

func (w *Window) updateCreateUserState() {
	if gui.Button(w.createUserScene.acceptButton.bounds, w.createUserScene.acceptButton.text) {
		w.createUserScene.Reset()
		email := w.createUserScene.emailInput.text
		password := w.createUserScene.passwordInput.text
		rePassword := w.createUserScene.rePasswordInput.text
		ruleLevel := w.createUserScene.ruleLevelInput.text
		name := w.createUserScene.nameInput.text
		surname := w.createUserScene.surnameInput.text

		//check input
		if len(email) <= 0 || len(password) <= 0 ||
			len(rePassword) <= 0 || len(ruleLevel) <= 0 ||
			len(name) <= 0 || len(surname) <= 0 {
			w.createUserScene.isError = true
			w.createUserScene.errorMessage = "Zero length error"
			return
		}
		lvl, err := strconv.Atoi(ruleLevel)
		// TODO curr max lvl
		if lvl > 5 || err != nil {
			w.createUserScene.isError = true
			w.createUserScene.errorMessage = "Bad ruleLVL input"
			return
		}
		newUser := User.NewUser(email, password, int32(lvl), name, surname)
		resp := w.ctx.Request(w.serverPID, &Proto.CreateUser{User: newUser}, WaitTime)
		val, err := resp.Result()
		if err != nil {

			w.createUserScene.isError = true
			w.createUserScene.errorMessage = "Actor ctx error"
		}
		if _, ok := val.(*Proto.AcceptCreateUser); ok {
			w.createUserScene.isAccept = true
			w.createUserScene.acceptMessage = "Created successfully"
			w.createUserScene.emailInput.text = ""
			w.createUserScene.passwordInput.text = ""
			w.createUserScene.rePasswordInput.text = ""
			w.createUserScene.ruleLevelInput.text = ""
			w.createUserScene.nameInput.text = ""
			w.createUserScene.surnameInput.text = ""
		}
		if _, ok := val.(*Proto.DenyCreateUser); ok {
			w.createUserScene.isError = true
			w.createUserScene.errorMessage = "DB deny!"
		}
	}

}

func (w *Window) renderCreateUserState() {
	//error
	if w.createUserScene.isError {
		rl.DrawText(w.createUserScene.errorMessage, 0, 0, 32, rl.Red)
	}
	if w.createUserScene.isAccept {
		rl.DrawText(w.createUserScene.acceptMessage, 0, 0, 32, rl.Green)

	}

	if rl.IsMouseButtonPressed(rl.MouseButtonLeft) {
		mousePos := rl.GetMousePosition()
		w.createUserScene.emailInput.focus = false
		w.createUserScene.passwordInput.focus = false
		w.createUserScene.rePasswordInput.focus = false
		w.createUserScene.ruleLevelInput.focus = false
		w.createUserScene.nameInput.focus = false
		w.createUserScene.surnameInput.focus = false

		if rl.CheckCollisionPointRec(mousePos, w.createUserScene.emailInput.bounds) {
			w.createUserScene.emailInput.focus = true
		}
		if rl.CheckCollisionPointRec(mousePos, w.createUserScene.passwordInput.bounds) {
			w.createUserScene.passwordInput.focus = true

		}
		if rl.CheckCollisionPointRec(mousePos, w.createUserScene.rePasswordInput.bounds) {
			w.createUserScene.rePasswordInput.focus = true

		}
		if rl.CheckCollisionPointRec(mousePos, w.createUserScene.ruleLevelInput.bounds) {
			w.createUserScene.ruleLevelInput.focus = true

		}
		if rl.CheckCollisionPointRec(mousePos, w.createUserScene.nameInput.bounds) {
			w.createUserScene.nameInput.focus = true

		}
		if rl.CheckCollisionPointRec(mousePos, w.createUserScene.surnameInput.bounds) {
			w.createUserScene.surnameInput.focus = true
		}
	}
	gui.TextBox(w.createUserScene.emailInput.bounds, &w.createUserScene.emailInput.text, w.createUserScene.emailInput.textSize, w.createUserScene.emailInput.focus)
	gui.TextBox(w.createUserScene.passwordInput.bounds, &w.createUserScene.passwordInput.text, w.createUserScene.passwordInput.textSize, w.createUserScene.passwordInput.focus)
	gui.TextBox(w.createUserScene.rePasswordInput.bounds, &w.createUserScene.rePasswordInput.text, w.createUserScene.rePasswordInput.textSize, w.createUserScene.rePasswordInput.focus)
	gui.TextBox(w.createUserScene.ruleLevelInput.bounds, &w.createUserScene.ruleLevelInput.text, w.createUserScene.ruleLevelInput.textSize, w.createUserScene.ruleLevelInput.focus)
	gui.TextBox(w.createUserScene.nameInput.bounds, &w.createUserScene.nameInput.text, w.createUserScene.nameInput.textSize, w.createUserScene.nameInput.focus)
	gui.TextBox(w.createUserScene.surnameInput.bounds, &w.createUserScene.surnameInput.text, w.createUserScene.surnameInput.textSize, w.createUserScene.surnameInput.focus)

}

janicaleksander avatar Jul 31 '25 09:07 janicaleksander