[Coding] safer UILabel creation and .text updates?

  • We have updated our Community Code of Conduct. Please read through the new rules for the forum that are an integral part of Paradox Interactive’s User Agreement.

operation40

Private
Mar 18, 2015
14
0
So, I'm getting this error in my mod..
Read from location 00000041 caused an access violation.

Location changes ofc..

This happens on either creating the label or setting the text:
myStatsTextField = myStatsWindowPanel.AddUIComponent<UILabel>();
or
myStatsTextField.text = TextFields;

Frustrating thing is it's not consistant.. sometimes it will run for 5-10 minutes, other times it crashes immediately.
I suspect some kind of thread contention going on -- ie: game is updating the UIComponent at the same time I'm trying to..

Is there a safer way to do these calls?

Code:
========== OUTPUTING STACK TRACE ==================

000000013FFF30F4 (Cities) ShaderLab::FastPropertyName::Init
000000013FFF50B5 (Cities) ShaderLab::FastPropertyName::Init
000000013FFF542B (Cities) ShaderLab::FastPropertyName::Init
000000013FFA645B (Cities) Unity::Material::SetTexture
000000014003B683 (Cities) MaterialPropertyBlock::GetHash
0000000015F1BC01 (Mono JIT Code) (wrapper managed-to-native) UnityEngine.Material:SetTexture (int,UnityEngine.Texture)
0000000015F1BB48 (Mono JIT Code) UnityEngine.Material:SetTexture (string,UnityEngine.Texture)
0000000015F1BACA (Mono JIT Code) UnityEngine.Material:set_mainTexture (UnityEngine.Texture)
0000000015F1B91F (Mono JIT Code) ColossalFramework.UI.UIDynamicFont:get_material ()
0000000015F1B802 (Mono JIT Code) ColossalFramework.UI.UIDynamicFont:get_isValid ()
0000000015F1B018 (Mono JIT Code) ColossalFramework.UI.UILabel:Invalidate ()
0000000015F16FD3 (Mono JIT Code) ColossalFramework.UI.UIComponent:InitializeComponent ()
0000000015F16DD7 (Mono JIT Code) ColossalFramework.UI.UIComponent:OnEnable ()
0000000015F343FD (Mono JIT Code) ColossalFramework.UI.UITextComponent:OnEnable ()
0000000004FA8BBB (Mono JIT Code) (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)
000007FEDC3E3DF7 (mono) mono_set_defaults
000007FEDC3382A1 (mono) mono_runtime_invoke
000000013FF65633 (Cities) ScriptingArguments::AddString
000000013FF65257 (Cities) ScriptingArguments::AddString
000000013FE8D670 (Cities) ReportScriptingObjectsTransfer::operator=
000000013FE9018B (Cities) GetMonoBehaviourInConstructor
000000013FE90567 (Cities) GetMonoBehaviourInConstructor
000000013FF18439 (Cities) DestroyObjectHighLevel
000000013FF186A0 (Cities) DestroyObjectHighLevel
000000013FDECDDC (Cities) CollisionMeshData::AwakeFromLoadThreaded
000000014004668A (Cities) Mesh::GetRuntimeMemorySize
0000000004FDC0FB (Mono JIT Code) (wrapper managed-to-native) UnityEngine.GameObject:Internal_AddComponentWithType (System.Type)
0000000004FDC045 (Mono JIT Code) UnityEngine.GameObject:AddComponent (System.Type)
0000000004FE1861 (Mono JIT Code) UnityEngine.GameObject:AddComponent<object> ()
0000000015F8CEB4 (Mono JIT Code) ColossalFramework.UI.UIComponent:AddUIComponent<object> ()
0000000062AFA0A0 (Mono JIT Code) CSLStatsPanel.StatusWindowInterface:updateText (System.Collections.Generic.List`1<string>)
0000000062A9E1F0 (Mono JIT Code) CSLStatsPanel.ThreadingCSLStatsMod:getstats2 ()
0000000062A9A83D (Mono JIT Code) CSLStatsPanel.ThreadingCSLStatsMod:OnAfterSimulationTick ()

Thanks!
 

operation40

Private
Mar 18, 2015
14
0
When it crashes on create, the dump is like the above (on setTexture)

When it crashes on .text update, I get this:
Code:
000000013FD40F49 (Cities) ScriptMapper::VirtualRedirectTransfer
000000013FD35990 (Cities) Unity::Material::BuildProperties
000000013FD35A12 (Cities) Unity::Material::SetShader
000000013FDCB5E3 (Cities) MaterialPropertyBlock::GetHash
0000000015DEBEFB (Mono JIT Code) (wrapper managed-to-native) UnityEngine.Material:set_shader (UnityEngine.Shader)
0000000015DEBAF1 (Mono JIT Code) ColossalFramework.UI.UIDynamicFont:get_material ()
0000000015DEB992 (Mono JIT Code) ColossalFramework.UI.UIDynamicFont:get_isValid ()
0000000015DEB1A8 (Mono JIT Code) ColossalFramework.UI.UILabel:Invalidate ()
0000000015E1FE6D (Mono JIT Code) ColossalFramework.UI.UILabel:OnTextChanged ()
0000000015E0413A (Mono JIT Code) ColossalFramework.UI.UILabel:set_text (string)

full output_logs:
crash on creating UILabel: http://pastebin.com/phMvijsg
crash on .text update: http://pastebin.com/nRqMTuM8
 

Zarine

Field Marshal
134 Badges
Feb 28, 2007
4.830
684
  • Europa Universalis IV: Res Publica
  • Gettysburg
  • Hearts of Iron III
  • Heir to the Throne
  • Europa Universalis III Complete
  • Knights of Pen and Paper +1 Edition
  • Leviathan: Warships
  • The Kings Crusade
  • Magicka
  • Majesty 2
  • Majesty 2 Collection
  • March of the Eagles
  • Europa Universalis III Complete
  • For the Motherland
  • Victoria: Revolutions
  • Rome Gold
  • Semper Fi
  • Sengoku
  • Ship Simulator Extremes
  • Sword of the Stars
  • Supreme Ruler: Cold War
  • The Showdown Effect
  • Victoria 2
  • Victoria 2: A House Divided
  • Victoria 2: Heart of Darkness
  • Hearts of Iron IV: No Step Back
  • Crusader Kings II: Sword of Islam
  • Arsenal of Democracy
  • Hearts of Iron II: Armageddon
  • Cities in Motion
  • Cities in Motion 2
  • Crusader Kings II
  • Crusader Kings II: Charlemagne
  • Crusader Kings II: Legacy of Rome
  • Crusader Kings II: The Old Gods
  • Crusader Kings II: Rajas of India
  • Crusader Kings II: The Republic
  • Crusader Kings II: Sons of Abraham
  • Crusader Kings II: Sunset Invasion
  • A Game of Dwarves
  • Commander: Conquest of the Americas
  • Darkest Hour
  • East India Company Collection
  • Europa Universalis III
  • Europa Universalis III Complete
  • Divine Wind
  • Europa Universalis IV
  • Europa Universalis IV: Art of War
  • Europa Universalis IV: Conquest of Paradise
  • Europa Universalis IV: Wealth of Nations
I have some UILabel and I have no problem at all to put a text in it with ".text = value;"
I also have no problem creating it.

I had none crashing issue (CSL catchs it I guess) when I had my panel or my label as variable but I forgot to initialize it (or wrongly done it).

If it help, here is part of my code for it:

Code:
    public class modSlider : UISlider
    {
        public UILabel _valueDisplayer;

        public void setSlider(modPanel panel, string Title,int reference, int min, int max, zarineButton associatedButton)
        {
            _valueDisplayer = (UILabel)panel.AddUIComponent(typeof(UILabel));
            this.value = reference;
           // stuff unrelated
        }

        protected override void OnValueChanged()
        {
            this._valueDisplayer.text = this.value.ToString();
        }

I have a slider setting the value and updating the UILabel each time there is a change. And so far no problem at all.
 

nik1t0zz

Private
44 Badges
Aug 31, 2009
15
0
  • Steel Division: Normand 44 Sign-up
  • Mount & Blade: Warband
  • Cities: Skylines - After Dark
  • Hearts of Iron IV Sign-up
  • Hearts of Iron IV: Cadet
  • Stellaris: Digital Anniversary Edition
  • Cities: Skylines - Natural Disasters
  • Hearts of Iron IV: Together for Victory
  • Cities: Skylines - Mass Transit
  • Knights of Honor
  • Cities: Skylines - Snowfall
  • Hearts of Iron IV: Death or Dishonor
  • Cities: Skylines - Parklife Pre-Order
  • Cities: Skylines - Parklife
  • Shadowrun Returns
  • Shadowrun: Dragonfall
  • Shadowrun: Hong Kong
  • Cities: Skylines Industries
  • Imperator: Rome
  • Prison Architect
  • Cities: Skylines
  • War of the Roses
  • Warlock: Master of the Arcane
  • The Showdown Effect
  • Darkest Hour
  • Cities: Skylines Deluxe Edition
  • Dungeonland
  • Europa Universalis III
  • Europa Universalis IV
  • Europa Universalis IV: Conquest of Paradise
  • For the Motherland
  • Hearts of Iron III
  • Hearts of Iron III: Their Finest Hour
  • Stellaris: Synthetic Dawn
  • Leviathan: Warships
  • Victoria: Revolutions
  • Semper Fi
  • Stellaris - Path to Destruction bundle
  • Victoria 2
  • Victoria 2: A House Divided
  • Stellaris: Leviathans Story Pack
  • Victoria 2: Heart of Darkness
  • Stellaris
  • Crusader Kings II
In both your (new) crash logs you can find this at the end:

0000000076F159ED (kernel32) BaseThreadInitThunk
000000007704C541 (ntdll) RtlUserThreadStart

That's the problem with thread safety. Can you upload the whole method that crashes (the one that updates the text)?
 
Last edited:

operation40

Private
Mar 18, 2015
14
0
this method takes a list of strings and makes a label for each one, but keeps the labels in a list so it doesn't recreate them next time

CSLStatsPanelLabel is just a regular UILabel.. I haven't defined any overrides for it

Code:
    public class CSLStatsWindowPanel : UIPanel
    {
        public List<CSLStatsPanelLabel> m_textfields = new List<CSLStatsPanelLabel>();

        public void updateText(List<string> TextFields)
        {
            statlog.log("update text initialized=" + initialized.ToString() + " running=" + running.ToString());
            if (!initialized) return;
            if (running) return;
            running = true;
            string s = ""; bool usesinglefield = true; // <-- work around uses a single label
            
            CSLStatsPanelLabel myStatsTextField;
            for (int i = 0;  i < TextFields.Count; i++)
            {
                if (i >= m_textfields.Count && (!usesinglefield || i == 0))
                {  // create a new UILabel
                    myStatsTextField = this.AddUIComponent<CSLStatsPanelLabel>();
                    myStatsTextField.name = "CSLStatsLabel_" + i.ToString();
                    myStatsTextField.autoSize = true;

                    m_textfields.Add(myStatsTextField);
                }
                else 
                { // use an existing UILabel
                    if (usesinglefield)
                    {
                        s += TextFields[i] + "\n";
                        myStatsTextField = m_textfields[0];
                    }
                    else myStatsTextField = m_textfields[i];
                }

                if (!usesinglefield)
                {
                    statlog.log("setting name: " + myStatsTextField.name + " text:  " + TextFields[i]);
                    myStatsTextField.text = TextFields[i];  // <-- crashy
                }
            }

            if (usesinglefield) m_textfields[0].text = s; // <-- works great

            running = false;
        }
}

I'd prefer individual labels so I can get creative with resizing and auto arranging, but it's not looking so bad with just 1 label:
http://steamcommunity.com/sharedfiles/filedetails/?id=410645446
 

Zarine

Field Marshal
134 Badges
Feb 28, 2007
4.830
684
  • Europa Universalis IV: Res Publica
  • Gettysburg
  • Hearts of Iron III
  • Heir to the Throne
  • Europa Universalis III Complete
  • Knights of Pen and Paper +1 Edition
  • Leviathan: Warships
  • The Kings Crusade
  • Magicka
  • Majesty 2
  • Majesty 2 Collection
  • March of the Eagles
  • Europa Universalis III Complete
  • For the Motherland
  • Victoria: Revolutions
  • Rome Gold
  • Semper Fi
  • Sengoku
  • Ship Simulator Extremes
  • Sword of the Stars
  • Supreme Ruler: Cold War
  • The Showdown Effect
  • Victoria 2
  • Victoria 2: A House Divided
  • Victoria 2: Heart of Darkness
  • Hearts of Iron IV: No Step Back
  • Crusader Kings II: Sword of Islam
  • Arsenal of Democracy
  • Hearts of Iron II: Armageddon
  • Cities in Motion
  • Cities in Motion 2
  • Crusader Kings II
  • Crusader Kings II: Charlemagne
  • Crusader Kings II: Legacy of Rome
  • Crusader Kings II: The Old Gods
  • Crusader Kings II: Rajas of India
  • Crusader Kings II: The Republic
  • Crusader Kings II: Sons of Abraham
  • Crusader Kings II: Sunset Invasion
  • A Game of Dwarves
  • Commander: Conquest of the Americas
  • Darkest Hour
  • East India Company Collection
  • Europa Universalis III
  • Europa Universalis III Complete
  • Divine Wind
  • Europa Universalis IV
  • Europa Universalis IV: Art of War
  • Europa Universalis IV: Conquest of Paradise
  • Europa Universalis IV: Wealth of Nations
I think at some point you might try to set myStatsTextField to an uninitialized object with "else myStatsTextField = m_textfields;" thus when reaching "myStatsTextField.text = TextFields;" it goes *BOOM"

Your "bool usesinglefield = true;" avoiding you to enter this part might be a good sign that it might be the problem.
 

nik1t0zz

Private
44 Badges
Aug 31, 2009
15
0
  • Steel Division: Normand 44 Sign-up
  • Mount & Blade: Warband
  • Cities: Skylines - After Dark
  • Hearts of Iron IV Sign-up
  • Hearts of Iron IV: Cadet
  • Stellaris: Digital Anniversary Edition
  • Cities: Skylines - Natural Disasters
  • Hearts of Iron IV: Together for Victory
  • Cities: Skylines - Mass Transit
  • Knights of Honor
  • Cities: Skylines - Snowfall
  • Hearts of Iron IV: Death or Dishonor
  • Cities: Skylines - Parklife Pre-Order
  • Cities: Skylines - Parklife
  • Shadowrun Returns
  • Shadowrun: Dragonfall
  • Shadowrun: Hong Kong
  • Cities: Skylines Industries
  • Imperator: Rome
  • Prison Architect
  • Cities: Skylines
  • War of the Roses
  • Warlock: Master of the Arcane
  • The Showdown Effect
  • Darkest Hour
  • Cities: Skylines Deluxe Edition
  • Dungeonland
  • Europa Universalis III
  • Europa Universalis IV
  • Europa Universalis IV: Conquest of Paradise
  • For the Motherland
  • Hearts of Iron III
  • Hearts of Iron III: Their Finest Hour
  • Stellaris: Synthetic Dawn
  • Leviathan: Warships
  • Victoria: Revolutions
  • Semper Fi
  • Stellaris - Path to Destruction bundle
  • Victoria 2
  • Victoria 2: A House Divided
  • Stellaris: Leviathans Story Pack
  • Victoria 2: Heart of Darkness
  • Stellaris
  • Crusader Kings II
He would see the NullReferenceException in the console output if that would be the case, no?
EDIT: Probably a IndexOutOfRangeException though.
 
Last edited:

nik1t0zz

Private
44 Badges
Aug 31, 2009
15
0
  • Steel Division: Normand 44 Sign-up
  • Mount & Blade: Warband
  • Cities: Skylines - After Dark
  • Hearts of Iron IV Sign-up
  • Hearts of Iron IV: Cadet
  • Stellaris: Digital Anniversary Edition
  • Cities: Skylines - Natural Disasters
  • Hearts of Iron IV: Together for Victory
  • Cities: Skylines - Mass Transit
  • Knights of Honor
  • Cities: Skylines - Snowfall
  • Hearts of Iron IV: Death or Dishonor
  • Cities: Skylines - Parklife Pre-Order
  • Cities: Skylines - Parklife
  • Shadowrun Returns
  • Shadowrun: Dragonfall
  • Shadowrun: Hong Kong
  • Cities: Skylines Industries
  • Imperator: Rome
  • Prison Architect
  • Cities: Skylines
  • War of the Roses
  • Warlock: Master of the Arcane
  • The Showdown Effect
  • Darkest Hour
  • Cities: Skylines Deluxe Edition
  • Dungeonland
  • Europa Universalis III
  • Europa Universalis IV
  • Europa Universalis IV: Conquest of Paradise
  • For the Motherland
  • Hearts of Iron III
  • Hearts of Iron III: Their Finest Hour
  • Stellaris: Synthetic Dawn
  • Leviathan: Warships
  • Victoria: Revolutions
  • Semper Fi
  • Stellaris - Path to Destruction bundle
  • Victoria 2
  • Victoria 2: A House Divided
  • Stellaris: Leviathans Story Pack
  • Victoria 2: Heart of Darkness
  • Stellaris
  • Crusader Kings II
So you did try commenting the logging out and can confirm it still crashes? Because it's not really clear if you did.

You could try putting everything in try-catch and have a catch like this:
Code:
catch (Exception ex)
{
    statlog.log(ex);
    statlog.log(ex.ToString());
}
That could at least catch what you doing wrong. It's weird that you are not getting any errors caught by the game, as it's usually what happens.
 
Last edited:

operation40

Private
Mar 18, 2015
14
0
to end all doubt I created a new mod which only tries to create labels on a panel

http://pastebin.com/EDyk9jyJ

it tries to create 30 labels in a UIPanel..

int mymaxlabelcount = 30;
You can delay crashing if you start that at 1 and increment by 1 on each call (instead of spamming them all at once), but it will still crash..

*edit* -- I don't get near 30 before crashing - I can get 3-6 on screen most of the time, then.. crash!

afaik -- memory access violations are a hard stop, and cannot be "caught".. but in my actual mod I have the method call wrapped in a try/catch anyway...
 
Last edited:

KnightHawkTFC

Lt. General
1 Badges
Mar 5, 2015
1.357
319
  • Cities: Skylines
Look I don't know jack about working with the Unity.UI yet... but that pastebin looked a little umm off for what you were trying to test\do once I tried it out.
Mainly cause that test rig is rebuilding nearly everything on like *every single tick.

Here is a working sample adapted from your original testrig paste bin. This one works, or at least it seems too just fine, tested up to 20 ran for 30min without issue.
The key difference is the UIPanel and List of UILabels is built only once at thread creation via OnCreated() and stored in static mylabels, then just the labels are updated every ~3 seconds instead of rebuilding them every tick. Left some comments in the code. Again I'm no ninja but I hope it helps ya.

http://pastebin.com/UqTEZ5Tr
 

operation40

Private
Mar 18, 2015
14
0
got your UI code in my mod now and it's working great !!

http://steamcommunity.com/sharedfiles/filedetails/?id=411503256

thanks again for all your help.. my onaftersimulationtick that checked tickcount % 100 seems to be responsible for all the crashing, cause I"m using the same updateText method that I pasted above and it's adding labels and updating them just fine now. your OnUpdate with framecount check seems way more stable
 

KnightHawkTFC

Lt. General
1 Badges
Mar 5, 2015
1.357
319
  • Cities: Skylines
Your quite welcome, your issue actually gave me a chance to play around with gui stuff for the first time. Glad to here it's working for you, while that %100 thing definitely seemed like a problem to me what also definitely helped was moving the creation of the labels (vs. the updating) out from under the calls that executed on the frame updates though I kind of assumed that was just a 'bug' of the down and dirty sample\test code, and technically maybe it might have worked but if it did probably would have caused unnecessary perf issues. I see in you real code your already doing it right (only creating once or when necessary) or so it looked. :)
 
Last edited: