How can i AddGadgetItem in MDIGadget using dll
How can i AddGadgetItem in MDIGadget using dll
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
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
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
Let's have a bit of fun with this one
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:
Run this code as a test:
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!

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
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
Last edited by netmaestro on Sun Apr 05, 2009 4:31 am, edited 7 times in total.
BERESHEIT
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
You've right. I thank there's a problem with this syntax:
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)
Code: Select all
Debug Hex(RegisterWindowMessage_("Hello") )
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)
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
Without threadsafe, the error occurs RANDOMLY in these lines:
I never have these problems usually. As I don't use threads very often, I suppose it is that.
Code: Select all
EventID = WaitWindowEvent()
Code: Select all
EndProcedure ; Proc control panel
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
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:
With:
Now, even if you switch on the threadsafe, I suppose it works fine. On my old computer, no more bug...
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
Code: Select all
Delay(1)
EventID = WindowEvent()
If EventID
...
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
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 :
And the client :
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.
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
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
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.
- netmaestro
- PureBasic Bullfrog
- Posts: 8451
- Joined: Wed Jul 06, 2005 5:42 am
- Location: Fort Nelson, BC, Canada
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.
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
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 :
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!
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

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.