How can i AddGadgetItem in MDIGadget using dll

Just starting out? Need help? Post your questions and find answers here.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Your input to this has been a big help. I believe we have the problem narrowed down to OpenLibrary - CallFunction and I admit I'm beginning to suspect the reliability of these commands as compared to Import. I haven't tried using prototypes on this yet. There's no denying that with code that "ought to work" (unless I'm missing something but I don't think so) the OpenLibrary - CallFunction version was getting occasional random crashes while the Import version - on exactly the same threaded logic - seems bulletproof. In fact, I was able to remove the callback and go back to a WaitWindowEvent() loop in the dll and it's still solid. I had replaced it because I thought maybe the two WaitWindowEvent loops were interfering with each other. Theoretically, with threadsafety on, you should be able to run many WaitWindowEvent loops at once as long as each deals only with the window(s) created in its own thread. A lot of effort went into making PureBasic threadsafe and I believe it can be relied upon. And sure enough it turns out not to be the culprit here.

I like the simplicity of the subclass-it-and-forget-it approach, and it does seem reliable enough, but the logic of it just doesn't seem trustworthy to me. Of course you can't do it in a main program because there has to be a wait loop somewhere keeping everything going, but in a loaded library it can just sit and wait because the main program has that. But - and this is what bothers me - If you click the menu item and bring up the control panel window, then close the main window, what exactly is supervising the orderly shutdown of the control panel window? In my threaded approach, I'm managing all that but in the subclass-it-and-forget-it version, is it safe to just chop its head off when it might be in the middle of something? I mean, there's nothing in this particular app that would be time-consuming in the message-handling of the control panel window, it's going to be sitting waiting for a message in most if not all cases, but in a larger app there might be more going on. A sudden death might just not be very clean. I just feel safer if I've got my bases covered.
..having a dll throw up it's own GUI in a separate thread is perhaps not a good idea and this will very likely impinge on some PB internals!
Shouldn't be the case imho. Also, consider this:

Though I'm one of PureBasic's nocturnals
I never would touch its internals
It's for my own sake
my code would soon break
'Cause language updates are eternal.

Ok, I'm going to bed now.

[edit] I'd like it if a team member would weigh in on this. I don't want to be casting aspersions on CallFunction or threadsafety if my coding is actually at fault.
Last edited by netmaestro on Sun Apr 05, 2009 6:15 am, edited 1 time in total.
BERESHEIT
Ollivier
Enthusiast
Enthusiast
Posts: 281
Joined: Mon Jul 23, 2007 8:30 pm
Location: FR

Post by Ollivier »

Excuse me if I penetrate again in the discussion which began, if I remember with a pair of big tits.

But I want to suggest you these two codes. The first is the DLL code to compile, and the second code is the main one.

What are your conclusions? Does it crash again?

MyDLL.DLL

Code: Select all

Structure MainInfo
   ThreadQty.I
   ThreadCheckID.I
   WindowNo.I
   MenuNo.I
   MDINo.I
   MenuFileQuit.I
   MenuFileNew.I
   MenuFileClose.I
   EventWin.I
   EventID.I
   MenuID.I
   Quit.I
EndStructure

   
ProcedureDLL ThreadCheck(*T.MainInfo)
   Delay(1)
   Repeat
      Delay(200)
   Until *T\ThreadQty < 1 
EndProcedure 


ProcedureDLL WindowObject(*T.MainInfo)
   Delay(1)
   *T\ThreadQty + 1
   *T\WindowNo = OpenWindow(-1, 0, 0, 400, 300, "", $CF0001)
   *T\MenuNo = CreateMenu(-1, WindowID(*T\WindowNo) )
   OpenSubMenu("Object")
      *T\MenuFileNew   = 1
      *T\MenuFileClose = 2
      *T\MenuFileQuit  = $FFF
      MenuItem(*T\MenuFileNew, "New")
      MenuItem(*T\MenuFileClose, "Close")
      MenuBar()
      MenuItem(*T\MenuFileQuit, "Quit")
   CloseSubMenu()
   *T\MDINo = MDIGadget(-1, 0, 0, 0, 0, 2, 256, #PB_MDI_AutoSize | #PB_MDI_BorderLess | #PB_MDI_NoScrollBars)
   Repeat
      Delay(1)
      *T\EventID = WindowEvent()
      If *T\EventID
         *T\EventWin = EventWindow()
         Select *T\EventWin
            Case *T\WindowNo
               Select *T\EventID
                  Case #PB_Event_CloseWindow
                     *T\Quit | 1
                  Case #PB_Event_Menu
                     *T\MenuID = EventMenu()
                     Select *T\MenuID
                        Case *T\MenuFileNew
                           AddGadgetItem(*T\MDINo, -1, "Text")
                        Case *T\MenuFileClose
                           If GetGadgetState(*T\MDINo) <> -1
                              CloseWindow(GetGadgetState(*T\MDINo) )
                           EndIf
                        Case *T\MenuFileQuit
                           *T\Quit | 1
                     EndSelect
               EndSelect   
            Default
               Select *T\EventID
                  Case #PB_Event_CloseWindow
                     CloseWindow(*T\EventWin)
               EndSelect   
         EndSelect
      EndIf
   Until *T\Quit
   CloseWindow(*T\WindowNo)
   *T\ThreadQty - 1
EndProcedure

Main code:

Code: Select all

;_______________________
;  OLLIVIER APRIL 2009
;  THREAD TEST - XP
;¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯


EnableExplicit


Structure MainInfo
   ThreadQty.I
   ThreadCheckID.I
   WindowNo.I
   MenuNo.I
   MDINo.I
   MenuFileQuit.I
   MenuFileNew.I
   MenuFileClose.I
   EventWin.I
   EventID.I
   MenuID.I
   Quit.I
EndStructure


   Define MainInfo.MainInfo
   If OpenLibrary(0, "MyDLL.DLL")
      Delay(1)
      If GetFunction(0, "WindowObject")
         CreateThread(GetFunction(0, "WindowObject"), MainInfo)
         MainInfo\ThreadCheckID = CreateThread(GetFunction(0, "ThreadCheck"), MainInfo)
         WaitThread(MainInfo\ThreadCheckID)
      EndIf
      CloseLibrary(0)
   EndIf
   
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

@Netmaestro; as far as I can see it, the subclassing version tidies up very well by simply using CloseWindow() in the appropriate place etc. PB will free all gadgets at that point. If the user closes the main window then, well #WM_NCDESTROY will allow you to free up any memory. Sure there may be some residual memory left over because the dll hasn't had an opportunity to issue the CloseWindow() command, but then your dll can utilise a function similar to your 'WrapUp' function for this.

It's no problem; my nxReport library administers preview windows in this manner. If the user closes the main application window (without closing a preview window) then, well they still have to destroy the underlying report object (nxReport exports an OOP interface). When nxReport is asked to destroy a report object it will first check for any open preview windows etc. No different than 'WrapUp'.

I use this subclassing approach quite often in dll's and have had no problems; not even with multi-threaded applications. I have had problems, however, when using WaitWindowEvent() and such in a dll; enough problems to now steer away from this! :wink: I admit that the last time I tried this was with PB 4.2 though.

I guess we'll have to agree to disagree as to the 'best approach' here when requiring a dll to throw up it's own GUI! I simply do not like the idea of two message retrieval loops running within two threads within the same process at the same time. :)

@Ollivier; sorry mate, I'll leave it to netmaestro to test your code as I now have to format my hard drive as a last resort to remove the cursed AVG. Gonna take me the best part of a day to bring my computer back up to scratch!
Last edited by srod on Sun Apr 05, 2009 12:51 pm, edited 1 time in total.
I may look like a mule, but I'm not a complete ass.
User avatar
hallodri
Enthusiast
Enthusiast
Posts: 208
Joined: Tue Nov 08, 2005 7:59 am
Location: Germany
Contact:

Post by hallodri »

simple but dirty :twisted:

DLL

Code: Select all

Import "" 
  PB_Gadget_Objects.integer
  PB_Window_Objects.integer
EndImport 

ProcedureDLL Install(WObject,GObject) 
  PB_Window_Objects\i = WObject 
  PB_Gadget_Objects\i = GObject   
EndProcedure 

ProcedureDLL AddNew(gadget)
  Static i
  
  win = AddGadgetItem(gadget,-1,"from dll")
  
  old = UseGadgetList(WindowID(win))
  
  ButtonGadget(-1,10,10,75,23,"moep - " + Str(i))
  
  UseGadgetList(old)
  
  i + 1
  
EndProcedure 
program

Code: Select all

Import "test.lib" 
  
  Install(WObject,GObject) 
  
  AddNew(gadget) 
  
  PB_Gadget_Objects.i
  PB_Window_Objects.i
EndImport 

  
Install(PB_Window_Objects,PB_Gadget_Objects)

window = OpenWindow(#PB_Any,#PB_Ignore,#PB_Ignore,640,480,"Test")

MDIGadget(0,0,0,640,480,0,0)

AddNew(0)
AddNew(0)

If window
  
  Repeat
    event = WaitWindowEvent()
    
    If event = #PB_Event_CloseWindow
      If EventWindow() = window
        Break
      Else
        CloseWindow(EventWindow())  
      EndIf
    EndIf   
    
    If event = #PB_Event_Gadget
      
      Debug GetGadgetText(EventGadget())
      Debug "---------------"
      
    EndIf 
    
  ForEver 
  
  End
  
EndIf 
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

That is dirty!!!!

:)
I may look like a mule, but I'm not a complete ass.
Ollivier
Enthusiast
Enthusiast
Posts: 281
Joined: Mon Jul 23, 2007 8:30 pm
Location: FR

Post by Ollivier »

sRod wrote:@Ollivier; sorry mate, I'll leave it to netmaestro to test your code as I now have to format my hard drive as a last resort to remove the cursed AVG. Gonna take me the best part of a day to bring my computer back up to scratch!
I never format a harddrive in the night between saturday and sunday. There is so much others hard things in these hours that the risk to loose memory is very high!

I hope NetMaestro could test it to know if it fails or if it succeed.

Ollivier
Ollivier
Enthusiast
Enthusiast
Posts: 281
Joined: Mon Jul 23, 2007 8:30 pm
Location: FR

Post by Ollivier »

@NetMaestro

Ups! I didn't the recent edition of your post.

Ollivier
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

@Ollivier: I'm sorry I haven't had a chance to adequately test your code. To test its reliability takes having a good long play with it and trying every combination of window creation/closing many times. I have run it for a few minutes and I must say it seems quite solid. It's also well-written and well-designed. It's very good work.

@srod:
I guess we'll have to agree to disagree as to the 'best approach' here
Not at all, I don't have your experience with gui's from dlls. I'm just theorizing on what I think should work. In the end, if and when I find myself needing to create a production dll with a gui I'll probably follow your formula.
BERESHEIT
Ollivier
Enthusiast
Enthusiast
Posts: 281
Joined: Mon Jul 23, 2007 8:30 pm
Location: FR

Post by Ollivier »

@NetMaestro

Thank you! However... There is again one bug in my code!

1) I must insert a Delay statement after each createthread statement.
Normally it gives it:

Code: Select all

Procedure Lambda(*T.Info)
Delay(1) ; First line in the procedure
...

Repeat
Delay(1)
...
ForEver
EndProcedure

CreateThread(@Lambda(), Info)
Delay(1) ; And just after the thread creation
...

Repeat
Delay(1)
...
ForEver
2) I haven't evidences WaitWindowEvent is more adapted than Delay() + WindowEvent() so I never use it.

3) All the memory I used is allocated in structured buffer. I don't use globals.

I think if these rules are kept, there is no bug! Importation can be added, etc...

Ollivier
Post Reply