-
Notifications
You must be signed in to change notification settings - Fork 18
Create and Communicate Window in Child Thread
Ward edited this page Apr 13, 2020
·
3 revisions
Here is the demonstration of window creation in child thread. After a window is created, it can communicate with the window in main thread by SendMessage/PostMessage.
import threadpool, winim/lean, sets
import wNim/[wApp, wFrame, wButton]
const
wEvent_RegisterChildFrame = wEvent_App + 1
wEvent_UnregisterChildFrame = wEvent_App + 2
wEvent_Ping = wEvent_App + 3
wEvent_Pong = wEvent_App + 4
var
app = App()
frame = Frame(title="main", size=(400, 300))
button1 = Button(frame, label="Create")
button2 = Button(frame, label="Ping")
childFrames: HashSet[HWND]
frame.wEvent_Size do ():
frame.autolayout """
H: |[button1..2]|
V: |[button1][button2(button1)]|
"""
proc createChildThread(hMain: HWND) {.thread.} =
{.gcsafe.}:
let threadId = GetCurrentThreadId()
echo threadId, " thread started"
var app = App()
var frame = Frame(title="child", size=(400, 300))
SendMessage(hMain, wEvent_RegisterChildFrame, WPARAM frame.handle, 0)
frame.wEvent_Ping do ():
echo threadId, " wEvent_Ping"
PostMessage(hMain, wEvent_Pong, WPARAM threadId, 0)
frame.wEvent_Destroy do ():
SendMessage(hMain, wEvent_UnregisterChildFrame, WPARAM frame.handle, 0)
frame.show()
app.mainLoop()
echo threadId, " thread closed"
button1.wEvent_Button do ():
spawn createChildThread(frame.handle)
button2.wEvent_Button do ():
for hwnd in childFrames:
PostMessage(hwnd, wEvent_Ping, 0, 0)
frame.wEvent_RegisterChildFrame do (event: wEvent):
childFrames.incl HWND event.wParam
frame.wEvent_UnregisterChildFrame do (event: wEvent):
childFrames.excl HWND event.wParam
frame.wEvent_Pong do (event: wEvent):
echo event.wParam, " wEvent_Pong"
frame.center()
frame.show()
app.mainLoop()
Another way is use HWND_BROADCAST. It is easier but a system-wide message must be registed.
import threadpool, winim/lean
import wNim/[wApp, wFrame, wButton]
var
wEvent_Ping = RegisterWindowMessage("wEvent_Ping")
wEvent_Pong = RegisterWindowMessage("wEvent_Pong")
var
app = App()
frame = Frame(title="main", size=(400, 300))
button1 = Button(frame, label="Create")
button2 = Button(frame, label="Ping")
frame.wEvent_Size do ():
frame.autolayout """
H: |[button1..2]|
V: |[button1][button2(button1)]|
"""
proc createChildThread() {.thread.} =
{.gcsafe.}:
let threadId = GetCurrentThreadId()
echo threadId, " thread started"
var app = App()
var frame = Frame(title="child", size=(400, 300))
frame.wEvent_Ping do ():
echo threadId, " wEvent_Ping"
PostMessage(HWND_BROADCAST, wEvent_Pong, WPARAM threadId, 0)
frame.show()
app.mainLoop()
echo threadId, " thread closed"
button1.wEvent_Button do ():
spawn createChildThread()
button2.wEvent_Button do ():
PostMessage(HWND_BROADCAST, wEvent_Ping, 0, 0)
frame.wEvent_Pong do (event: wEvent):
echo event.wParam, " wEvent_Pong"
frame.center()
frame.show()
app.mainLoop()
A Win32's event object can be used as a signal to communicate with child thread without a window.
import threadpool, os, winim/lean
import wNim/[wApp, wFrame, wButton]
const wEvent_Pong = wEvent_App + 1
var
app = App()
frame = Frame(title="main", size=(400, 300))
button = Button(frame, label="Ping")
hEventPing = CreateEvent(nil, false, false, nil)
proc signal(hEvent: HANDLE) {.inline.} =
SetEvent(hEvent)
proc isSignaled(hEvent: HANDLE): bool =
if WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0:
ResetEvent(hEvent)
result = true
proc createChildThread(hEventPing: HANDLE, hMain: HWND) {.thread.} =
while true:
if hEventPing.isSignaled():
echo "ping"
PostMessage(hMain, wEvent_Pong, 0, 0)
os.sleep(1)
button.wEvent_Button do ():
hEventPing.signal()
frame.wEvent_Pong do ():
echo "pong"
spawn createChildThread(hEventPing, frame.handle)
frame.center()
frame.show()
app.mainLoop()
To check the event object in main thread, we need a timer or a hook proc of mainloop.
import threadpool, os, winim/lean
import wNim/[wApp, wFrame, wButton]
var
hEventPing = CreateEvent(nil, false, false, nil)
hEventPong = CreateEvent(nil, false, false, nil)
proc signal(hEvent: HANDLE) {.inline.} =
SetEvent(hEvent)
proc isSignaled(hEvent: HANDLE): bool =
if WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0:
ResetEvent(hEvent)
result = true
var
app = App()
frame = Frame(title="main", size=(400, 300))
button = Button(frame, label="Ping")
proc createChildThread(hEventPing, hEventPong: HANDLE) {.thread.} =
while true:
if hEventPing.isSignaled():
hEventPong.signal()
echo "ping"
os.sleep(1)
button.wEvent_Button do ():
hEventPing.signal()
app.addMessageLoopHook() do (msg: var wMsg, modalHwnd: HWND) -> int:
if hEventPong.isSignaled():
echo "pong"
spawn createChildThread(hEventPing, hEventPong)
frame.center()
frame.show()
app.mainLoop()