I'm working on a script and observed a weird behavior and wanted to share it with you guys. I deleted everything which isn't necessary - that's why the structure of the script might seems confuse.
Situation
I use a DotNet DataGridView and fill it via MultiThreading (CSharpUtilities.SynchronizingBackgroundWorker) with data.
Problem
When i start the script and do a
manual garbage collection afterwards (enter gc() in the script listener and hit enter), i get this error in some cases:
An unknown error occurred while MAXScript was
performing garbage collection.
If you get this error multiple times, recommend restarting max
Test Results
I found several places in the script which i can change to fix the problem. I can't explain why that is, but i want to share the knowledge with you and maybe you can explain it to me or it helps other people in the future.
1. Copy this code and execute it in 3Ds Max
2. Do a manual garbage collection (via gc() in the script listener)
3. You should get the error message (see above)
(
pathsArray = #()
tasksArray = #()
threadsArray = #()
rollout gckick "gckick" width:448 height:400
(
dotNetControl lbxResult "System.Windows.Forms.DataGridView" pos:[8,60] width:432 height:336
fn addThread threadArgumentArray = (
--create thread
myThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
myThread.WorkerSupportsCancellation = true
functionName = threadArgumentArray[1] --the Array is 2dimensional 1st value=function 2nd value=path for scannings
dotnet.addEventHandler myThread "DoWork" functionName
--add threads to management arrays
append tasksArray threadArgumentArray
append threadsArray myThread
--get the thread ID based on the thread array
tid = tasksArray.count
--start the thread
myThread.runWorkerAsync tid
)
fn resizeList = (
gckick.height = 600 + 60
--WORKS: gckick.height = 660
)
fn addItemImage = (
resizeList()
)
on gckick open do (
lbxResult.SelectionMode = (dotNetClass "System.Windows.Forms.DataGridViewSelectionMode").FullRowSelect
--Add Columns
imgCol = dotNetObject "System.Windows.Forms.DataGridViewImageColumn"
imgCol.width = 100
imgCol.image = myImg
lbxResult.columns.add imgCol
infoCol = dotNetObject "System.Windows.Forms.DataGridViewTextBoxColumn"
infoCol.headerText = "Info"
infoCol.width = (lbxResult.width - 100)
lbxResult.columns.add infoCol
--ADD Content
rowID = lbxResult.rows.add()
lbxResult.rows.item[0].cells.item[1].value = @"D:\Simon\Projekte\3D\egosoft\interiors_rooms_ar_engibay.max"
--Start a multithreaded function
addThread #(addItemImage,"")
--WORKS: addItemImage()
)
)
createDialog gckick
)
Workaround #1
(Not really a solution because it will disable the multithreading, but at least you don't get the GC error anymore)
1. Restart 3Ds Max
2. Change the line
addThread #(addItemImage,"") to
addItemImage() in the
on gckick open do (-section.
3. Execute the script
4. When i do a manueal garbage collection now, i DON'T get any error
Workaround #2
1. Restart 3Ds Max (and revert all your previous script-changes if you made any)
2. Change the line
gckick.height = 600 + 60 to
gckick.height = 660 in the
resizeList-function.
3. Execute the script
4. When i do a manueal garbage collection now, i DON'T get any error
I have no idea why the GC doesn't drop an error after the change in the resizeList function. But I'm happy that i found a "solution" and hope this post will help future people. Or someone can explain what's going on here.
And i want note a really good read when you want to work in a performant wa y with the dotnet DataGrid:
http://msdn.microsoft.com/en-us/library/ha5xt0d9.aspxSolution #1
Kind of a solution was to not call the resizeList() function directly from the multi threaded function addItemImage() since this seems to produce problems. I used a timer which is started before i call the multithreaded function and this timer checks a global bool-variable. This variable is set to true by the multithreaded function when it's done. Then the timer reacts to the now true variable, calls resizeList() and deactives itself again.
(
pathsArray = #()
tasksArray = #()
threadsArray = #()
fillListThreadDone = false
rollout gckick "gckick" width:448 height:400
(
dotNetControl lbxResult "System.Windows.Forms.DataGridView" pos:[8,60] width:432 height:336
timer tThreadObserver "Timer" pos:[424,24] width:24 height:24 active:false interval:200
fn addThread threadArgumentArray = (
--create thread
myThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
myThread.WorkerSupportsCancellation = true
functionName = threadArgumentArray[1] --the Array is 2dimensional 1st value=function 2nd value=path for scannings
dotnet.addEventHandler myThread "DoWork" functionName
--add threads to management arrays
append tasksArray threadArgumentArray
append threadsArray myThread
--get the thread ID based on the thread array
tid = tasksArray.count
--start the thread
myThread.runWorkerAsync tid
)
fn resizeList = (
gckick.height = 600 + 60
--WORKS: gckick.height = 660
)
fn addItemImage = (
tThreadObserver.active = true
tThreadObserver.ticks = 0
)
on tThreadObserver tick do (
if (fillListThreadDone) then (
resizeList()
fillListThreadDone = false
tThreadObserver.active = false
)
)
on gckick open do (
lbxResult.SelectionMode = (dotNetClass "System.Windows.Forms.DataGridViewSelectionMode").FullRowSelect
--Add Columns
imgCol = dotNetObject "System.Windows.Forms.DataGridViewImageColumn"
imgCol.width = 100
imgCol.image = myImg
lbxResult.columns.add imgCol
infoCol = dotNetObject "System.Windows.Forms.DataGridViewTextBoxColumn"
infoCol.headerText = "Info"
infoCol.width = (lbxResult.width - 100)
lbxResult.columns.add infoCol
--ADD Content
rowID = lbxResult.rows.add()
lbxResult.rows.item[0].cells.item[1].value = @"D:\Simon\Projekte\3D\egosoft\interiors_rooms_ar_engibay.max"
--Start a multithreaded function
addThread #(addItemImage,"")
--WORKS: addItemImage()
)
)
createDialog gckick
)