Page 1 of 2
How can i AddGadgetItem in MDIGadget using dll
Posted: Sat Mar 21, 2009 12:33 am
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
Posted: Sat Apr 04, 2009 3:00 am
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
Posted: Sat Apr 04, 2009 4:50 pm
by netmaestro
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:
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!
Posted: Sat Apr 04, 2009 7:24 pm
by Ollivier
@NetMaestro
Your secretary with big tits forgot the character @ line 15 just before Msg$ between the brackets in the main code.
Ollivier
Posted: Sat Apr 04, 2009 7:31 pm
by netmaestro
I don't understand your response. What character is missing? It runs "as is" here.

Posted: Sat Apr 04, 2009 7:49 pm
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)
Posted: Sat Apr 04, 2009 7:53 pm
by netmaestro
I can reproduce it if I enable Threadsafe in the DLL but not otherwise.
Recommendation: Don't do that. (for now)
Posted: Sat Apr 04, 2009 7:56 pm
by Ollivier
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.
Posted: Sat Apr 04, 2009 7:58 pm
by netmaestro
Recopy the code as it was updated earlier. The dll no longer uses WaitWindowEvent(), it's now WaitWindowEvent(1).
Posted: Sat Apr 04, 2009 8:10 pm
by Ollivier
Ok. I copy it...
Posted: Sat Apr 04, 2009 8:31 pm
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...
Posted: Sat Apr 04, 2009 10:45 pm
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?
Posted: Sat Apr 04, 2009 11:09 pm
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.
Posted: Sun Apr 05, 2009 12:47 am
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.
Posted: Sun Apr 05, 2009 2:12 am
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!