From: Tom A. <to...@yt...> - 2001-02-28 21:16:56
|
With regards to fork() and Win32::GUI.... I have been playing with this as well, and while I have not arrived at a "best practices" I have arrived at a baseline that works. Basically, I fork the gui thread and then send window messages to it, or, call the appropriate function on the window object I want to manipulate in a "classless" way. For example, if you had created a progress bar control, you would normally call it using $progressControl->somefunction(); that is $progressControl->SetPos($amount); As it turns out, and I don't know if Aldo intended for it to work this way, if you have a handle to the control, you can also call the routine in the "classless" way by doing GUI::ProgressBar::SetPos($progressControlHandle,$amount)); The latter is the way I talk to the controls from the thread that is not running Win32::GUI::Dialog. Right now, I am locating the window handle for the dialog itself using GUI:FindWindow() in the non-gui thread. I'm not happy with this approach, and am looking at other solutions as well. Amine Moulay Ramdane's Win32::MemMap package (http://www.generation.net/~aminer/Perl/) looks like the right solution for this, at least for me, but I had a problem with it when I tried to use it and ran out of time to mess with it. I know he recently posted an updated version, I need to pull that down and see if it still has the same problem. My initial need was to not have to use GUI::FindWindow() to locate the dialog and controls. My thought was to serialize a hash in the gui thread and pass it back to the non gui thread via shared memory - which Win32::MemMap provides, and unserialize on the non gui side. You can probably do something similar with pipes, but for some reason, I thought that the shared memory approach looked better. I suspect that I've rambled on enough for one day. I've included in this message a script for a progress bar that runs in a thread by itself taking positioning commands from another thread. Oh, by the way, in response to another message on the list today, I think Win32::MemMap has a timer implementation in it. Wandering back to the real world..... --- Tom Tom Allebrandi to...@yt... #! perl -w ############################################################################ #### ############################################################################ #### ## ## Subpackage ProgressBar ## ## package Ta2::UiHelpers::ProgressBar; require Exporter; @ISA = qw(Exporter); @EXPORT = qw( Create Destroy Set ); use Win32::GUI; use constant CW_USEDEFAULT => 0x80000000; # # Reserves ProgressBar in the main namespace for us (uhmmm...) # *ProgressBar:: = \%Ta2::UiHelpers::ProgressBar::; ############################################################################ #### # # Manifest constants # use constant TITLE_BAR_HEIGHT => 19; use constant WINDOW_BORDER => 3; use constant MARGIN => (7 - WINDOW_BORDER); ############################################################################ #### # # Create() # sub Create { # # Parameters # my ($title,$width,$height,$minValue,$maxValue) = @_; # # "Local" variables # my $handle = 0; my $i; my $pid; # # Fire off a thread to manage the window # $pid = fork(); if (!defined($pid)) { # # Failed to fork # warn("Failed to fork management thread for ProgressBar\n"); } elsif ($pid == 0) { # # We are continuing in the child. Do the progress bar # doProgressBar($title,$width,$height,$minValue,$maxValue); exit(0); } else { # # We are continuing in the parent. Wait for the window to appear but # wait no more than 30 seconds # $i = 0; while (($handle == 0) && ($i < 30)) { # # Look for the window # if (($handle = GUI::FindWindow("",$title)) == 0) { # # Not found. Pause and try again # sleep(1); $i++; } } if ($handle == 0) { warn("Never saw the ProgressBar appear\n"); } } return($handle); } ############################################################################ #### # # Destroy() # sub Destroy { # # Parameters # my ($handle) = @_; # # Tell the window procedure to quit # # 0x8001 = WM_EXITLOOP = WM_APP+1 return(GUI::PostMessage($handle,0x8001,-1,0)); } ############################################################################ #### # # Set() # sub Set { # # Parameters # my ($handle,$amount) = @_; # # The progress bar is the first control on the dialog # $handle = GUI::GetDlgItem($handle,0); # # Set the new progress amount # # 1026 == PBM_SETPOS return(GUI::ProgressBar::SetPos($handle,$amount)); } ############################################################################ #### # # doProgressBar() # sub doProgressBar { # # Parameters # my ($title,$width,$height,$minValue,$maxValue) = @_; # # "Local" variables # my $progressControl; my $window; # # Create a dialog box to hold the progress bar # $window = new Win32::GUI::DialogBox( -text => $title, -left => CW_USEDEFAULT, -top => CW_USEDEFAULT, -width => $width, -height => $height, -name => 'Ta2::UiHelpers::ProgressBarDialog', -remstyle => WS_SYSMENU, -remexstyle => WS_EX_CONTEXTHELP); if (!defined($window)) { warn("Failed to create ProgressBar dialog\n"); goto ABORT; } # # Compute a good size for the progress bar control # $width -= (2 * MARGIN) + (2 * WINDOW_BORDER); $height -= TITLE_BAR_HEIGHT + (2 * MARGIN) + (2 * WINDOW_BORDER); # # Put the progress bar on the window # $progressControl = $window->AddProgressBar( -left => MARGIN, -top => MARGIN, -width => $width, -height => $height, -smooth => 1); if (!defined($progressControl)) { warn("Failed to create ProgressBar control\n"); goto ABORT; } # # Set the limits and the initial position # $progressControl->SetRange($minValue,$maxValue); $progressControl->SetPos($minValue); # # Put the display on the screen # $window->Show(); # # Activate the dialog # Win32::GUI::Dialog(); # # Take the display off of the screen # $window->Hide(); ABORT: if (defined($window)) { $window->DestroyWindow(); } } ############################################################################ #### # # Support routines # sub ProgressBarDialog_Terminate { return(-1); } ############################################################################ #### ############################################################################ #### ## ## Main program ## ## # # "Local" variables # my $i; my $pb; # # Create a progress bar # $pb = Create("A Progress Bar",200,100,0,30); # # Update it once a second for 30 seconds # for ($i=0;$i<30;$i++) { Set($pb,$i); sleep(1); } # # Take away the progress bar # Destroy($pb); exit(0); __END__ |
From: Tom A. <to...@yt...> - 2001-02-28 23:17:04
|
>What advantage does this give you? The -> form searches the @ISA class >hierarchy but once it finds the function the two forms are equivilent. > >Is there something about use of threads which forbids object syntax? The >object form is safer and seems cleaner to me. Glenn got his reply in while I was typing mine. I think we are saying the same thing however... The problem here I think is that what we are commonly referring to as a "thread", isn't. It must be viewed as if it is a separate process. If you create an object in one "thread" (process), it does not exist in the other "thread" (process). I can't see any way to use the object syntax on an object that was not created in my "thread" (process). Maybe I missed something along the way. So, I resorted to the "classless" call mechanism. --- Tom Tom Allebrandi to...@yt... |
From: Glenn L. <Gle...@ne...> - 2001-03-01 01:27:22
|
Tom Allebrandi wrote: > The problem here I think is that what we are commonly referring to as a > "thread", isn't. It must be viewed as if it is a separate process. Well, from Windows point of view, it is a thread. From Perl's point of view, it is a process. The Perl interpreter is busily keeping the data separate between the two threads (I'm not sure I understand the complete technique of the magic that does that, but I'm sure it can be made to work because the Perl language doesn't expose "real" addresses (much)). On the other hand, the (Unix) model for "fork" is that the multiple processes (threads on Perl for Windows) start off with identical data/variables/file handles. And the Windows model for "windows" is that the windows are owned by a process (not a thread), and can be accessed by any thread that has the window handle. (And in fact, because Windows was developed on DOS, the windows are even a bit visible to other processes, but that doesn't concern us here.) So, I earlier alluded to some experimentation I was going to try... read on. > If you create an object in one "thread" (process), it does not exist in the > other "thread" (process). I can't see any way to use the object syntax on an > object that was not created in my "thread" (process). Maybe I missed > something along the way. By creating the Win32::GUI objects before forking, both the parent and child threads get copies (?) of the object variables. Because of the nature of Windows, the embedded Window handles inside both copies of the object variables are equally usable. Because of the (present) nature of Win32::GUI, whereby most of the parameter data is pumped into Win32 API parameters, and most of the return values are obtained by calling Win32 APIs to obtain it, I have shown experimentally that it is possible to use the Win32::GUI object references from both a parent and a child thread. Now it is important to remember that Windows only delivers window messages to the first thread of a program, so in the Perl "fork" environment, this gets translated to only the parent process of a group of Perl-forked processes can successfully run Win32::GUI::Dialog (Yep, I tried it the other way first, figuring that the parent could more easily monitor the child process for death, since fork returns the child pid, and waitpid works that way--but it just hung, and the windows never appeared). However, the child can use the object references created by Win32::GUI to access the "IsEnabled", "IsVisible" attributes of the window widgets, and they are dynamically updated (not cached in the object). The child can access the current selection from combo boxes. The child can enable and disable widgets, and the display gets updated appropriately. This is quite adequate for my application, which now can do its "long" operations in the child "process", and keep the GUI window "active" (except that certain parts get disabled during "long" operations). Thanks again, Tom, for broadening my outlook regarding Win32::GUI. Perhaps I've now returned the favor? -- Glenn ===== Even if you're on the right track, you'll get run over if you just sit there. -- Will Rogers |
From: Glenn L. <Gle...@ne...> - 2001-02-28 22:23:14
|
Tom Allebrandi wrote: > With regards to fork() and Win32::GUI.... > > I have been playing with this as well, and while I have not arrived at a > "best practices" I have arrived at a baseline that works. Basically, I fork > the gui thread and then send window messages to it, or, call the appropriate > function on the window object I want to manipulate in a "classless" way. Now this is a clever idea. I think that for the particular application I am working on, that this technique, or a variation, might suffice. Even without Timer to do polling. > For example, if you had created a progress bar control, you would normally > call it using > > $progressControl->somefunction(); > > that is > > $progressControl->SetPos($amount); > > As it turns out, and I don't know if Aldo intended for it to work this way, > if you have a handle to the control, you can also call the routine in the > "classless" way by doing > > GUI::ProgressBar::SetPos($progressControlHandle,$amount)); Well, I suspect it was intended to work that way, because it is documented (see the tutorial for how to eliminate the console window) that the methods can be called on any window, using the classless technique. Now a window created by one thread's Win32::GUI is, to another thread, simply one of "any" windows. But I hadn't put that information together that way, so I appreciate your helping to overcome my mental block. > The latter is the way I talk to the controls from the thread that is not > running Win32::GUI::Dialog. > > Right now, I am locating the window handle for the dialog itself using > GUI:FindWindow() in the non-gui thread. I'm not happy with this approach, > and am looking at other solutions as well. Sure. I wonder what would happen if a widget (progressbar or whatever) were created before the fork, if the class handles would be active for both after the fork? Of course, only the thread that called Win32::GUI::Dialog would see the messages and act on them... but that's precisely the desired behavior. Not sure what all sorts of timing/data race conditions doing that sort of thing would produce, but I may experiment with it some. > Amine Moulay Ramdane's Win32::MemMap package > (http://www.generation.net/~aminer/Perl/) looks like the right solution for > this, at least for me, but I had a problem with it when I tried to use it > and ran out of time to mess with it. I know he recently posted an updated > version, I need to pull that down and see if it still has the same problem. > > My initial need was to not have to use GUI::FindWindow() to locate the > dialog and controls. My thought was to serialize a hash in the gui thread > and pass it back to the non gui thread via shared memory - which > Win32::MemMap provides, and unserialize on the non gui side. > > You can probably do something similar with pipes, but for some reason, I > thought that the shared memory approach looked better. Shared memory vs. pipes vs. files even -- I'm indifferent to the bulk-data communication channel. But the thing that had me stumped was how to get the GUI process to "wake up and smell the roses" based on some outside event that doesn't manifest itself as a window message to one of the window processes... and you've relieved me of that mental block. So, a general communication mechanism can likely be created, at worst by creating a special widget in the GUI to which events can be posted from a thread which has the responsibility of monitoring the non-GUI world. Shucks, speaking of shared memory, one could even have a hidden text widget (or two) into which the data of interest gets sent. Shared memory may be fastest, of course. > I suspect that I've rambled on enough for one day. I've included in this > message a script for a progress bar that runs in a thread by itself taking > positioning commands from another thread. > > Oh, by the way, in response to another message on the list today, I think > Win32::MemMap has a timer implementation in it. But is that timer integrated into Win32::GUI? Is it clear how well it would interact with Win32::GUI? I'll be taking a look at MemMap. Thanks for your response. -- Glenn ===== Even if you're on the right track, you'll get run over if you just sit there. -- Will Rogers |
From: Eric B. <er...@eb...> - 2001-02-28 22:31:45
|
On Wed, 28 Feb 2001, Tom Allebrandi wrote: > As it turns out, and I don't know if Aldo intended for it to work this way, > if you have a handle to the control, you can also call the routine in the > "classless" way by doing > > GUI::ProgressBar::SetPos($progressControlHandle,$amount)); > > The latter is the way I talk to the controls from the thread that is not > running Win32::GUI::Dialog. What advantage does this give you? The -> form searches the @ISA class hierarchy but once it finds the function the two forms are equivilent. Is there something about use of threads which forbids object syntax? The object form is safer and seems cleaner to me. - Eric B. -- "Pasteurized From Concentrate" |
From: Glenn L. <Gle...@ne...> - 2001-02-28 23:02:56
|
Eric Bennett wrote: > On Wed, 28 Feb 2001, Tom Allebrandi wrote: > > > As it turns out, and I don't know if Aldo intended for it to work this way, > > if you have a handle to the control, you can also call the routine in the > > "classless" way by doing > > > > GUI::ProgressBar::SetPos($progressControlHandle,$amount)); > > > > The latter is the way I talk to the controls from the thread that is not > > running Win32::GUI::Dialog. > > What advantage does this give you? The -> form searches the @ISA class > hierarchy but once it finds the function the two forms are equivilent. > Is there something about use of threads which forbids object syntax? The > object form is safer and seems cleaner to me. The object syntax requires an object handle. It isn't clear to me how to obtain an object handle for a window created in one thread/process that can be used in a different thread/process. Perhaps, but I haven't experimented yet, if the two processes are the result of a fork after the object handle is obtained, that it would work to a certain degree, but perhaps not. The non-object syntax can be used by any thread/process on any window to which it can obtain a Windows window handle. Including windows created by Win32::GUI. Hnece the non-object syntax is more powerful. -- Glenn ===== Even if you're on the right track, you'll get run over if you just sit there. -- Will Rogers |