How can i AddGadgetItem in MDIGadget using dll

Just starting out? Need help? Post your questions and find answers here.
Mahmoud
New User
New User
Posts: 6
Joined: Fri Mar 13, 2009 12:36 am
Location: Cairo, Egypt

How can i AddGadgetItem in MDIGadget using dll

Post by Mahmoud »

Dear all,

I need to know a method to add a new window (AddGadgetItem) to an exist MDIGadget using function inside a loaded DLL file, i'm new to PureBasic world, so i hope any one can give me a detailed example of what the host file look like and also the DLL function.


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

Post by Ollivier »

Hi Mahmoud,

I don't really understand your problem. I think your wroten language is bad and my read language is bad too!!! But we can try to understand ourself...

Do you want to create a DLL procedure which add a child window in a MDI? Is my translation ok?

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 »

Let's have a bit of fun with this one :wink:

DLL code: First, compile this snippet to a shared dll called "ControlPanel.dll" with threadsafety turned on and make it available to the second snippet:

Code: Select all

; ControlPanel.dll 
;
; *** Compile THREADSAFE ***

ProcedureDLL AttachProcess(instance) 

  Global Message_AddWindow, tid, Quit, *MessageData 

  msg$ = "I'm applying for the manager job." 
  msg$ + "Have a seat." 
  msg$ + "You're hiring a secretary, so you ask candidates:" 
  msg$ + "What's 2 and 2?" 
  msg$ + "You hear:" 
  msg$ + " 1) 4." 
  msg$ + " 2) uh... 22?" 
  msg$ + " 3) is that Bitwise AND or arithmetic addition?" 
  msg$ + "Which one do you hire?" 
  msg$ + "The one with the big tits." 
  msg$ + "When can you start?" 
  
  Message_AddWindow = RegisterWindowMessage_(msg$) 
  
  ; Allocate space for transferring data 
  *MessageData = AllocateMemory(#MAX_PATH) 
    
EndProcedure 

ProcedureDLL DetachProcess(instance)
  FreeMemory(*MessageData) 
EndProcedure

Procedure GetWindowDetails(Caller_hwnd) 
  
  win = OpenWindow(#PB_Any, 0,0,440,60,"Control Panel",#PB_Window_SystemMenu|#PB_Window_ScreenCentered) 
  txt_g = TextGadget(#PB_Any,10,23,100,20,"New Window Name:") 
  WindowName_g = StringGadget(#PB_Any,110,20,200,20,"") 
  WindowAdd_g  = ButtonGadget(#PB_Any,320,20,100,20,"Add Window") 
  SetActiveGadget(WindowName_g) 
  
  quit=0
  Repeat 
    EventID = WaitWindowEvent(1)
    Select EventID
      Case #PB_Event_CloseWindow
        quit=#True
        
      Case #PB_Event_Gadget
        Select EventGadget()
          Case WindowAdd_g
            text$ = GetGadgetText(WindowName_g) 
            If text$ 
              PokeS(*MessageData, text$) 
              PostMessage_(Caller_hwnd, Message_AddWindow, 0, *MessageData) 
              SetGadgetText(WindowName_g, "") 
              SetActiveGadget(WindowName_g) 
            Else 
              MessageRequester("","You must supply a Window Name",#MB_ICONINFORMATION) 
            EndIf 
        EndSelect
    EndSelect            
  Until quit 
  
  CloseWindow(win) 

EndProcedure 

ProcedureDLL ControlPanel(hwnd) 
  tid = CreateThread(@GetWindowDetails(),hwnd) 
EndProcedure 

ProcedureDLL WrapUp() 
  quit = #True 
  If IsThread(tid)
    WaitThread(tid)
  EndIf
EndProcedure 
Run this code as a test:

Code: Select all

; ControlPanelTest.pb - calling program 

Import "controlpanel.lib" 
  ControlPanel(a.l) As "_ControlPanel@4" 
  WrapUp() As "_WrapUp@0" 
EndImport 

msg$ = "I'm applying for the manager job." 
msg$ + "Have a seat." 
msg$ + "You're hiring a secretary, so you ask candidates:" 
msg$ + "What's 2 and 2?" 
msg$ + "You hear:" 
msg$ + " 1) 4." 
msg$ + " 2) uh... 22?" 
msg$ + " 3) is that Bitwise AND or arithmetic addition?" 
msg$ + "Which one do you hire?" 
msg$ + "The one with the big tits." 
msg$ + "When can you start?" 
  
Global Message_AddWindow = RegisterWindowMessage_(msg$) 

#Main = 0 
#MDIChild = 1 
OpenWindow(#Main, 0, 0, 400, 300, "MDIGadget", #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_SizeGadget|#PB_Window_MaximizeGadget) 
CreateMenu(#Main, WindowID(#Main)) 
  MenuTitle("Menu index 0") 
    MenuItem(30, "Control Panel") 
  MenuTitle("MDI windows menu") 
    MenuItem(0, "self created item") 
    MenuItem(1, "self created item") 

MDIGadget(0, 0, 0, 0, 0, 1, 2, #PB_MDI_AutoSize) 
AddGadgetItem(0, #MDIChild, "child window") 
; add gadgets here... 
UseGadgetList(WindowID(#Main)) ; go back to the main window gadgetlist 

quit = 0 
Repeat 

  EventID = WaitWindowEvent() 

  If EventID 
    Select EventID 
    
      Case #PB_Event_CloseWindow 
        If EventWindow() = #Main 
          quit = #True 
        Else 
          CloseWindow(EventWindow()) 
        EndIf 
      
      Case #PB_Event_Menu 
        Select EventMenu() 
          Case 30 
            ControlPanel(WindowID(#Main)) 
             
        EndSelect 
      
      Case Message_AddWindow 
        name$ = PeekS(EventlParam()) 
        newwindow = AddGadgetItem(0, #PB_Any, name$) 
        
    EndSelect 
  EndIf 

Until quit 

CloseWindow(#Main) 
WrapUp()

End 
This is pretty much the simplest case. In practice you would probably structure the *MessageData memory block with lots more details about the window and how to build it. And when you're registering the window messages you should use funnier jokes. But this should get you started, have fun!
Last edited by netmaestro on Sun Apr 05, 2009 4:31 am, edited 7 times in total.
BERESHEIT
Ollivier
Enthusiast
Enthusiast
Posts: 281
Joined: Mon Jul 23, 2007 8:30 pm
Location: FR

Post by Ollivier »

@NetMaestro

Your secretary with big tits forgot the character @ line 15 just before Msg$ between the brackets in the main code.

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 »

I don't understand your response. What character is missing? It runs "as is" here. :?:
BERESHEIT
Ollivier
Enthusiast
Enthusiast
Posts: 281
Joined: Mon Jul 23, 2007 8:30 pm
Location: FR

Post by Ollivier »

You've right. I thank there's a problem with this syntax:

Code: Select all

Debug Hex(RegisterWindowMessage_("Hello") )
But no... I hope your secretary won't want to kill me...

I have an IMA when I exit the control panel. The problem is the line in which the error occurs is ... line 1 in the main code : commented line!

I test with and without threadsafe.

I don't find where is the problem (XP SP3 - PB4.30 x86)
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 »

I can reproduce it if I enable Threadsafe in the DLL but not otherwise.

Recommendation: Don't do that. (for now)
BERESHEIT
Ollivier
Enthusiast
Enthusiast
Posts: 281
Joined: Mon Jul 23, 2007 8:30 pm
Location: FR

Post by Ollivier »

Without threadsafe, the error occurs RANDOMLY in these lines:

Code: Select all

  EventID = WaitWindowEvent()

Code: Select all

EndProcedure ; Proc control panel
I never have these problems usually. As I don't use threads very often, I suppose it is that.
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 »

Recopy the code as it was updated earlier. The dll no longer uses WaitWindowEvent(), it's now WaitWindowEvent(1).
BERESHEIT
Ollivier
Enthusiast
Enthusiast
Posts: 281
Joined: Mon Jul 23, 2007 8:30 pm
Location: FR

Post by Ollivier »

Ok. I copy it...
Ollivier
Enthusiast
Enthusiast
Posts: 281
Joined: Mon Jul 23, 2007 8:30 pm
Location: FR

Post by Ollivier »

Okay, there were again bugs however they were less often.

I replace these the two same parts of code in dll code and in the main code:

Code: Select all

WaitWindowEvent() ; with or without args
With:

Code: Select all

   Delay(1)
   EventID = WindowEvent()
 
   If EventID
      ...
Now, even if you switch on the threadsafe, I suppose it works fine. On my old computer, no more bug...
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 »

I still get random errors with that change.

I've reworked the code, now I can't reproduce any error.

Can someone make it fail?

[edit] Drat, I just did. It took 40 tries though. Only if I close the main window while the dll window is up and the dll compiled threadsafe. Any ideas?
BERESHEIT
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

I get the same crash; on top of other problems.

What I do not understand is why you are using a thread in the dll? This is asking for trouble; mixing threads with GUI in the dll itself. I presume it is because you wish to run a separate event-loop in the dll? This is not required because you can simply fall back to the event-loop running in the client application. It is undoubtedly these two event loops (you are using a PB callback in the thread) which is causing the crash.

Remove the thread and I no longer get any crashes.

Here's the dll code I used :

Code: Select all

; ControlPanel.dll 

Global gOldProc
Global Caller_hwnd, Message_AddWindow, tid, win, Quit 
Global WindowName_g, WindowAdd_g, *MessageData

ProcedureDLL AttachProcess(instance) 
  msg$ = "heyho" 
  Message_AddWindow = RegisterWindowMessage_(msg$) 
  *MessageData = AllocateMemory(#MAX_PATH) 
EndProcedure 

ProcedureDLL DetachProcess(instance) 
  If *MessageData
    FreeMemory(*MessageData)
  EndIf
EndProcedure 

Procedure CallBack(hwnd, msg, wparam, lparam) 
  Select msg 
    Case #WM_CLOSE
      CloseWindow(win)
    Case #WM_COMMAND 
      If lparam = GadgetID(WindowAdd_g) 
        text$ = GetGadgetText(WindowName_g) 
        If text$ 
          PokeS(*MessageData, text$) 
          PostMessage_(Caller_hwnd, Message_AddWindow, 0, *MessageData) 
          SetGadgetText(WindowName_g, "") 
          SetActiveGadget(WindowName_g) 
        Else 
          MessageRequester("","You must supply a Window Name",#MB_ICONINFORMATION) 
        EndIf 
      EndIf 
    EndSelect    
  ProcedureReturn CallWindowProc_(gOldProc, hwnd, msg, wparam, lparam)
EndProcedure 

ProcedureDLL ControlPanel(hwnd) 
  Caller_hwnd = hwnd 
  win = OpenWindow(#PB_Any, 0,0,440,60,"Control Panel",#PB_Window_SystemMenu|#PB_Window_ScreenCentered) 
  gOldProc = SetWindowLong_(WindowID(win), #GWL_WNDPROC, @CallBack()) 
  txt_g = TextGadget(#PB_Any,10,23,100,20,"New Window Name:") 
  WindowName_g = StringGadget(#PB_Any,110,20,200,20,"") 
  WindowAdd_g  = ButtonGadget(#PB_Any,320,20,100,20,"Add Window") 
  SetActiveGadget(WindowName_g) 
EndProcedure 
And the client :

Code: Select all

; ControlPanelTest.pb - calling program 

msg$ = "heyho" 
  
Global Message_AddWindow = RegisterWindowMessage_(msg$) 

Global lib = OpenLibrary(#PB_Any, "test.dll") 

#Main = 0 
#MDIChild = 1 
OpenWindow(#Main, 0, 0, 400, 300, "MDIGadget", #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_SizeGadget|#PB_Window_MaximizeGadget) 
CreateMenu(#Main, WindowID(#Main)) 
  MenuTitle("Menu index 0") 
    MenuItem(30, "Control Panel") 
  MenuTitle("MDI windows menu") 
    MenuItem(0, "self created item") 
    MenuItem(1, "self created item") 

MDIGadget(0, 0, 0, 0, 0, 1, 2, #PB_MDI_AutoSize) 
AddGadgetItem(0, #MDIChild, "child window") 
; add gadgets here... 
UseGadgetList(WindowID(#Main)) ; go back to the main window gadgetlist 

quit = 0 
Repeat 

  EventID = WaitWindowEvent() 

  If EventID 
    Select EventID 
    
      Case #PB_Event_CloseWindow 
        If EventWindow() = #Main 
          quit = #True 
        Else 
          CloseWindow(EventWindow()) 
        EndIf 
      
      Case #PB_Event_Menu 
        Select EventMenu() 
          Case 30 
           If lib 
             CallFunction(lib, "ControlPanel", WindowID(#Main) ) 
           EndIf 
        EndSelect 
      
      Case Message_AddWindow 
        name$ = PeekS(EventlParam()) 
        newwindow = AddGadgetItem(0, #PB_Any, name$) 
        
    EndSelect 
  EndIf 

Until quit 

If IsLibrary(lib) 
  CloseLibrary(lib) 
EndIf 
Note the subclassing in the dll to avoid using a PB callback. I have found that these are best avoided in dll's.

The window procedure in the dll will receive messages provided there is some message retrieval code running (getting messages and despatching them etc.) and we can simply leave that to the event loop in the client app. You don't really want two of these things running at the same time.
I may look like a mule, but I'm not a complete ass.
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 »

Thanks for your help, it's appreciated. I don't have many dll's with gui's in them. What I find confusing is that I don't understand the real why of it. In theory, my threaded approach shouldwork imho. My PeekMessage loop is only processing those messages directed to the dll's window and the thread isn't dealing with anything it shouldn't have access to. Exactly what is clashing with what?

I really would like it if you or someone else could point out to me exactly why I can't do what I did, and why it should cause a problem.
BERESHEIT
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Okay, it seems to be a problem in the way that PB is tidying up and freeing resources (memory etc.)

In the client application, I get no errors with your code (touch wood) if I switch the OpenLibrary() / CallFunction() business for direct imports :

Code: Select all

; ControlPanelTest.pb - calling program 

Import "test.lib"
  ControlPanel(a.l) As "_ControlPanel@4"
  WrapUp() As "_WrapUp@0"
EndImport


msg$ = "heyho" 
  
Global Message_AddWindow = RegisterWindowMessage_(msg$) 

#Main = 0 
#MDIChild = 1 
OpenWindow(#Main, 0, 0, 400, 300, "MDIGadget", #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_SizeGadget|#PB_Window_MaximizeGadget) 
CreateMenu(#Main, WindowID(#Main)) 
  MenuTitle("Menu index 0") 
    MenuItem(30, "Control Panel") 
  MenuTitle("MDI windows menu") 
    MenuItem(0, "self created item") 
    MenuItem(1, "self created item") 

MDIGadget(0, 0, 0, 0, 0, 1, 2, #PB_MDI_AutoSize) 
AddGadgetItem(0, #MDIChild, "child window") 
; add gadgets here... 
UseGadgetList(WindowID(#Main)) ; go back to the main window gadgetlist 

quit = 0 
Repeat 

  EventID = WaitWindowEvent() 

  If EventID 
    Select EventID 
    
      Case #PB_Event_CloseWindow 
        If EventWindow() = #Main 
          quit = #True 
        Else 
          CloseWindow(EventWindow()) 
        EndIf 
      
      Case #PB_Event_Menu 
        Select EventMenu() 
          Case 30 
           ControlPanel(WindowID(#Main)) 
        EndSelect 
      
      Case Message_AddWindow 
        name$ = PeekS(EventlParam()) 
        newwindow = AddGadgetItem(0, #PB_Any, name$) 
        
    EndSelect 
  EndIf 

Until quit 

CloseWindow(#Main) 

WrapUp() 

End 
I cannot really explain this except to reiterate that 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! Better to have the dll throw up its GUI within the same thread as the calling application etc. :)

Mind you if the use of Import / EndImport does indeed fix this code then it does confirm that there is nothing inherently wrong with what you are doing; just that somewhere along the line we are indeed stepping on PB's toes!
I may look like a mule, but I'm not a complete ass.
Post Reply