I first noticed it when creating a simple list of 256 icons in a Combogadget - some of the icons were previous icons, not the new one it should've been, and there was an element of randomness, and there was no logical reason for this. Here for example every icon is a grayscale shade from 0-255, but you can see what happens when handle id's get reused (even though they were newly CreateImage'd):

I isolated that problem - AddGadgetItem() only adds a new image to the list (comctl32's "ImageList" in case of Windows) if that ImageId() hasn't been used before. Even if it's a brand new CreateImage'd image, and with different content, if the handle ID has ever been used before then AddGadgetItem() will refer to cache, ignoring the new image. It seems like there is an internal list of previous handle id's to use as a cache.
I'm guessing this is a well-intended feature to save memory/time, but has turned out a big problem for me. But if it's an intended feature it would probably be also trivial to fix, I hope!
If this is fixed:
1) there would be no problems worrying about colliding handles (the image bug above wouldnt exist)
2) the same image could be used (eg. Box(newcolor)) rather than FreeImage/CreateImage'ing hundreds/thousands of times - it's a great SPEED boost too.
To work around this as it stands we would have to detect if a CreateImage'd handle has been used before (by keeping our own internal list), and if we ever encounter a used handle we'd have to essentially go into a CreateImage loop until a new handle was derived, and that might risk infinite looping... it all sounds a messy workaround anyway, and not a proper solution.
What was happening in my case is that sometimes, one or more of those 255 handles had already been used - even though they had been FreeImage'd and newly CreateImage'd. (whatever Windows uses for its "GetNewHandle" isn't a PRNG, and there is this chance of repeats). I tested on XP and Win7-64, both had the same problem -- every 2nd or 3rd run would exhibit handle collisions.
So I was wondering - is this just an OS limitation of the gadget itself? But using GDIView i confirmed that a list with 256 images is a Bitmap gdi object of height 4192 (width 16 of course), but only height 4032 when there were 7 colliding handles.
Using a debugger i then bpx'd on every ImageList_ api in comctl32 and confirmed that ImageList_Add is not being called if the image handle has been used before - but I see no reason for this, and if it called ImageList_Add there'd be no problem.
So that suggests to me its a PB bug and not OS, especially because the behavior is basically the same across all OS.
This limitation also means that every image added to an imagelist must essentially be CreateImage'd, but if you're creating for example color palettes all you want to do is repeatedly redraw the same image with Box(newcolor) - it's sooo much faster when theres thousands of images involved, but thats currently not a possibility because of this, so CreateImage must be called for each palette, but then that runs into the colliding handle issue. Stuck either way!

Simple example of trying to use the same image to add a Blue and Red icon. In this demo it uses the same image (just Box()'ing over the first image to draw the second image), but even if you change it to use FreeImage/CreateImage you then risk the colliding handle issue:
Code: Select all
#List1=1
If OpenWindow(0, 0, 0, 270, 80, "ComboBoxGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ComboBoxGadget(#List1, 10, 10, 250, 21, #PB_ComboBox_Image)
image = CreateImage(#PB_Any, 16,16)
If image And StartDrawing(ImageOutput(image))
Box(0, 0, 15, 15, RGB(255,0,0)) ;red
StopDrawing()
EndIf
AddGadgetItem(#List1, -1,"Red", ImageID(image))
If image And StartDrawing(ImageOutput(image))
Box(0, 0, 15, 15, RGB(0,0,255)) ;blue
StopDrawing()
EndIf
AddGadgetItem(#List1, -1,"Blue", ImageID(image))
;yes i've SaveImage'd both to ensure and confirm that they are indeed Red and Blue
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf

Here's the program i used to generate the 256 shades of grayscale (each image is FreeImage'd and the next newly CreateImage'd, so should have unique handle ID's but sometimes they collide). I usually only have to run it 2 or 3 times (tested XP/Win7) before i get collisions, which youll see in debug window, which demonstrates that even if you use newly CreateImage'd images there can still be problems due to this behavior:
Code: Select all
#MAXUNIQUEITEMS = 4096
Procedure.l Unique(lVal.l)
Static arCnt.l
Static Dim arItem.l(#MAXUNIQUEITEMS)
If arCnt = 0
Goto AddElement
EndIf
For i.l = 0 To arCnt-1
If arItem(i) = lVal
ProcedureReturn i+1 ;'// Not added (already exists/isn't unique)
EndIf
Next i
AddElement:
arItem(arCnt) = lVal
arCnt = arCnt + 1
ProcedureReturn 0 ;'// Added (is unique)
EndProcedure
Procedure CreateSwatchImage(Color)
Protected image = CreateImage(#PB_Any, 16,16)
If image And StartDrawing(ImageOutput(image))
Box(0, 0, 15, 15, RGB(Color,Color,Color)) ;grayscale
StopDrawing()
EndIf
ProcedureReturn image
EndProcedure
#List1=1
If OpenWindow(0, 0, 0, 270, 180, "ComboBoxGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ComboBoxGadget(#List1, 10, 70, 250, 21, #PB_ComboBox_Image)
For a = 0 To 255
hImg = CreateSwatchImage(a)
AddGadgetItem(#List1, -1,"Item #" + Str(a), ImageID(hImg))
oldpos = Unique(ImageID(hImg))
If oldpos
Debug(Str(a)+" exists as "+ Str(oldpos))
EndIf
If hImg
FreeImage(hImg)
EndIf
Next
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf