Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Win32::GUI sending Windows messages between threads [solved]

by shadrack (Acolyte)
on Jan 17, 2013 at 09:07 UTC ( [id://1013733]=perlquestion: print w/replies, xml ) Need Help??

shadrack has asked for the wisdom of the Perl Monks concerning the following question:

Running strawberry perl 5.16.2 under XP. I've got a multi-threaded program (parent thread runs the GUI, child is the "worker"). I want the child to send some sort of "wake up" signal to the parent when it has information to pass (via shared variables). The problem is the GUI thread is stuck running Win32::GUI::Dialog(), so it doesn't receive signals and polling isn't possible (not to mention inefficient). Anyway, it occurs to me that really all I have to do is send the parent a Windows message which I've set up to be handled by a custom function which does whatever I want it to do (in this case, it would process the information stored in the shared variables). WM_TIMER seemed to be a good candidate for this message, but I can't for the life of me figure out how to make this work. Here's the test, boiled down to the essentials:
#!perl use threads; use Win32::GUI qw( WM_CLOSE WM_TIMER ); $CHILD = threads->create('child', ''); $WinMain = Win32::GUI::Window->new( -name => 'Main', -text => 'Task Test', -width => 300, -height => 200, -onTerminate => \&Main_Terminate, -onTimer => \&DoTimer, ); $WinMain->Show(); Win32::GUI::Dialog(); $CHILD->detach(); sub Main_Terminate { -1; } sub child { sleep 1; # Give parent time to create window my $parent = Win32::GUI::FindWindow('','Task Test'); Win32::GUI::SendMessage($parent,WM_TIMER,0,0); threads->exit(0); } sub DoTimer { print "Timer fired\n"; return 0; }
If I run the above code, I never see the "Timer fired" message. However, if I change the SendMessage() call to use WM_CLOSE instead of WM_TIMER, the gui closes (as expected), which I take to be evidence that the theory is sound. I just can't figure out how to get it to work with WM_TIMER or some other message that I can intercept with my own handler.

Any suggestions?

Oh, and before somebody suggests it, yes I know polling is possible with DoEvents(), however under certain circumstances, DoEvents() can take an indefinite amount of time to return which makes the polling cycle too unpredictable for this particular application.


Update: I believe I've solved it. Thanks to roboticus' prodding, I dug a little deeper into trying to use WM_USER and I eventually figured it out. Set up the handler with $WinMain->Hook(WM_USER, \&coderef); (also you'd need to add WM_USER to the constants imported in "use Win32::GUI qw(...". I'm still not sure why it fails with WM_TIMER, but the end result solves my problem. Thanks all!

Replies are listed 'Best First'.
Re: Win32::GUI sending Windows messages between threads
by roboticus (Chancellor) on Jan 17, 2013 at 12:06 UTC

    shadrack:

    Sorry, I don't know how to solve your problem. I just wanted to warn you to use a custom windows message (e.g., WM_USER) rather than WM_TIMER. At some point you may decide to use a timer in your application, and your code may be in the way.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      Using WM_USER was my first thought, but I don't see how I can set up a handler for it. There doesn't seem to be an "-onUser" property for WM_USER in the way there in an "-onTimer" property for WM_TIMER.
Re: Win32::GUI sending Windows messages between threads
by BrowserUk (Patriarch) on Jan 17, 2013 at 15:44 UTC

    Use PostMessage() instead of SendMessage() and see how you fare?


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Same result with Postmessage (nothing happens).

      A quick Google indicates the only difference between PostMessage and SendMessage is that PostMessage returns immediately while SendMessage waits for the recipient to process the message.

      Anyway, I tried it. No difference.

        A quick Google indicates the only difference between PostMessage and SendMessage is that PostMessage returns immediately while SendMessage waits for the recipient to process the message.

        You are correct, but you have missed the significance.

        1. SendMessage is a synchronous call.

          If the timer handler -- your custom function -- takes a long time to run, it will cause your GUI to freeze until it is finished.

        2. PostMessage() is an asynchronous call;

          The message is added to the queue and processed when it reaches the top. This allowing the originator of the message to do other things during the interim.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Win32::GUI sending Windows messages between threads
by dasgar (Priest) on Jan 17, 2013 at 15:29 UTC

    Here's something that I personally would try.

    Launch a thread and create Win32::GUI::Timer object. When the timer goes off, have it check to see if the thread is joinable. If it's not, do nothing. If if it is, then you can join the thread and kill the Win32::GUI::Timer object.

    However, if you're needing the thread to continue running for a long time and return stuff periodically, you can tweak the above steps so that you're checking on shared variable(s) instead of checking to see if the thread is joinable.

      I definitely need the child thread to run for a long time -- possibly the entire lifetime of the program. Sounds like you're suggesting I essentially poll inside the timer handler. I am considering that as a fallback if I can't get the message passing thing to work, but ultimately it's polling and it's inefficient and the message passing approach seems much better, so I want to pursue it first.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1013733]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (8)
As of 2024-04-23 13:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found