Hi all,

I've been travelling on business for the last couple of weekes, and so have had a few spare moments at airports to have a look into these 2 issues, and think I have got to the root cause in both cases.

RichEdit crashing on exit: Fundamentally this is a problem with Win32::GUI, where the richedit dll can get unloaded before the richedit window objects get destroyed. In GUI.pm the richedit dll is loaded when the package is use()d, and there is an END block that is there to unload the dll when the process terminates. It is, unfortunately, very easy to end up with references to the richedit control (or any of its ancestor windows, as Win32::GUI puts a reference to each objects in its parent to allow the $main_window->Richedit1->dostuff() syntax) that prevent the object's reference counts going to zero when the script exits. The result of this is that the END block gets executed (and unloads the richedit dll) before the RichEdit's DESTROY method gets called.

Here is a  link that (may) explain how this happens better than I could:
http://groups.google.co.uk/group/comp.lang.perl.misc/browse_thread/thread/e85f1d86213fbaa4/520169ef5cab46c4

And here is an example showing it:

#!perl
use strict;
use warnings;

my $a = Thing->new();

warn "Exiting\n";

# Uncomment this next line for a safer way to get the DESTROY method called
# before the END block than calling it explicitly
# undef $a;

exit(0);

END {
 warn "Running END block\n";
}

# somesub causes the reference count to $a to not go to zero when the script
# exits, and so there is a reference to a Thing object still in existence,
# which results in the END block being executed before Thing's DESTROY
# method.
# Comment out this sub, and see Thing's DESTROY method being called before
# the END block is executed.
sub somesub {
 my $tmp = $a;
}

package Thing;

sub new {
 return bless {};
}

sub DESTROY {
 warn "DESTROYing Thing\n";
}
__END__

In GUI.pm the solution is to comment out the END block It is not necessary to call FreeLibrary explicitly, as windows will do this for you on process termination. While making this change I would move the richedit LoadLibrary call into the Win32::GUI::RichEdit's constructor (Win32::GUI::RichEdit::new()) so that the process only gets bloated with the richedit dll(s) if you are actually using a richedit control.

Here's a context diff for how I did this. I haven't given it much testing, but it seems OK on the small number of examples I've run:
cvs diff -c GUI.pm (in directory C:\Development\Work\Win32-GUI\)
Index: GUI.pm
===================================================================
RCS file: /cvsroot/perl-win32-gui/Win32-GUI/GUI.pm,v
retrieving revision 1.26
diff -c -r1.26 GUI.pm
*** GUI.pm    1 Feb 2005 11:04:06 -0000    1.26
--- GUI.pm    19 May 2005 16:29:54 -0000
***************
*** 1793,1798 ****
--- 1793,1799 ----
     # can also be called as PARENT->AddRichEdit(%OPTIONS).
     # See new Win32::GUI::Edit for B<%OPTIONS>
 sub new {
+ $Win32::GUI::RICHED = Win32::GUI::LoadLibrary("RICHED32") unless defined $Win32::GUI::RICHED; return Win32::GUI->_new(Win32::GUI::constant("WIN32__GUI__RICHEDIT", 0), @_);
 }

***************
*** 3243,3254 ****
     -widget  => "Splitter",
 );

! $Win32::GUI::RICHED = Win32::GUI::LoadLibrary("RICHED32");

! END {
     # print "Freeing library RICHED32\n";
!     Win32::GUI::FreeLibrary($Win32::GUI::RICHED);
! }

 #Currently Autoloading is not implemented in Perl for win32
# Autoload methods go after __END__, and are processed by the autosplit program.
--- 3244,3255 ----
     -widget  => "Splitter",
 );

! #$Win32::GUI::RICHED = Win32::GUI::LoadLibrary("RICHED32");

! #END {
     # print "Freeing library RICHED32\n";
! #    Win32::GUI::FreeLibrary($Win32::GUI::RICHED);
! #}

 #Currently Autoloading is not implemented in Perl for win32
# Autoload methods go after __END__, and are processed by the autosplit program.

** END of context diff **

Additionally I would strongly discourage calling the richedit object's DESTROY method directly. Not only is this bad OO practice (the DESTROY method is technically private), but also due to the way that Win32::GUI ties it's objects, the DESTROY sub usually gets called twice (once for untie, once for object destruction), and I can't get my head around what might be happening if it is called directly. It would (in my opinion) be better (as in my example above) to undef the 'global' variables you have holding the objects before you exit as a work-around. As a final comment on this issue, using the NEM, it is possible in most cases to avoid having to have these 'global' variables, as the window object gets passed into the event handler subs.

dialogui causing hang: This appears to be a microsoft 'bug' in IsDialogMessage(). Here are some descriptions of some similar problems:
http://www.guyswithtowels.com/articles/2002-08-15-1600.html#NestedDialogs
http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q149501
http://blogs.msdn.com/oldnewthing/archive/2004/07/30/201988.aspx

The solution is to add the WS_EX_CONTROLPARENT extended style to the TabFrame object.

Putting this all together here is my re-write of Leonard's original script, that runs correctly under Win32::GUI V1.0

#!perl
use strict;
#use warnings;

use Win32::GUI;
use Win32::GUI::TabFrame;

# Sadly this needs global scope, to stop the
# menu getting destroyed prematurely.
my $windowmenu = Win32::GUI::MakeMenu(
   "Item &1"         => "",
   " > SubItem &1"   => "Item1SubItem1",
   " > SubItem &2"   => "Item1SubItem2",
   " > SubItem &3"   => "Item1SubItem3",
   " > -"            => 0,
   " > E&xit"        => "AppExit",
   "Item &2"         => "",
   " > SubItem &1"   => "Item2SubItem1",
 );

my $mainwindow = create_mainwindow();

$mainwindow->Show();
Win32::GUI::Dialog();

exit(0);

sub create_mainwindow {
   my $windowheight = 400;
   my $windowwidth = 700;

   my $mw = Win32::GUI::Window->new(
       -name   => "Main",
       -width  => $windowwidth,
       -height => $windowheight,
       -title  => "Main Window",
       -menu   => $windowmenu,
       -onResize => \&MW_Resize,
       -dialogui => 1,
   );

   $mw->AddStatusBar(
       -name   => "StatusBar"
   );

   $mw->AddTreeView(
       -text      => "",
       -name      => "TreeView",
       -left      => -1,
       -top       => -1,
       -rootlines => 1,
       -buttons   => 1,
   );

 $mw->AddTabFrame(
   -name     => "TabFrame",
   -panel    => "TabPage",
   -tabstop  => 1,
   -pushexstyle => WS_EX_CONTROLPARENT,
 );

 $mw->TabFrame->InsertItem(
   -text   => "Tab1",
   -border => 1,
 );

 $mw->TabFrame->InsertItem(
   -text   => "Tab2",
   -border => 1,
 );

 $mw->TabFrame->InsertItem(
   -text   => "Tab3",
   -border => 1,
 );

 $mw->TabFrame->InsertItem(
   -text   => "Tab4",
   -border => 1,
 );

   $mw->TabFrame->TabPage0->AddRichEdit(
       -name      => "RichEdit1",
       -multiline => 1,
       -vscroll   => 1,
       -hscroll   => 1,
       -readonly  => 1,
       -tabstop => 1,
   );

   $mw->TabFrame->TabPage1->AddRichEdit(
       -name      => "RichEdit2",
       -multiline => 1,
       -vscroll   => 1,
       -readonly  => 1,
       -tabstop => 1,
   );

   $mw->TabFrame->TabPage2->AddListView(
       -name          => "ListView1",
       -vscroll => 1,
       -report => 1,
       -fullrowselect => 1,
       -tabstop => 1,
   );

   $mw->TabFrame->TabPage2->ListView1->InsertColumn(
       -index => 0,
       -width => 220,
       -text  => "Column 1",
   );

   $mw->TabFrame->TabPage2->ListView1->InsertColumn(
       -index => 1,
       -width => 220,
       -text  => "Column 2",
   );

   $mw->TabFrame->TabPage2->AddButton(
       -name    => "Button1",
       -text    => "Button 1",
       -default => 1,
       -tabstop => 1,
   );

   $mw->TabFrame->TabPage2->AddButton(
       -name    => "Button2",
       -text    => "Button 2",
       -tabstop => 1,
   );

   $mw->TabFrame->TabPage3-> AddListView(
       -name          => "ListView2",
       -vscroll => 1,
       -report => 1,
       -fullrowselect => 1,
       -tabstop => 1,
   );

   $mw->TabFrame->TabPage3->ListView2->InsertColumn(
       -index => 0,
       -width => 125,
       -text  => "Column1",
   );

   $mw->TabFrame->TabPage3->ListView2->InsertColumn(
       -index => 1,
       -width => 50,
       -text  => "Column2",
   );

   $mw->TabFrame->TabPage3->ListView2->InsertColumn(
       -index => 2,
       -width => 200,
       -text  => "Column3",
   );

   $mw->TabFrame->TabPage3->ListView2->InsertColumn(
       -index => 3,
       -width => 200,
       -text  => "Column4",
   );

   return $mw;
}


sub Button1_Click  {
   print "DEBUG - Button 1 was Clicked\n";
}


sub Button2_Click  {
   print "DEBUG - Button 2 was Clicked\n";
}


sub AppExit_Click  {
   -1;
}

sub MW_Resize  {
   my $self = shift;

   my $windowheight = $self->ScaleHeight();
   my $windowwidth = $self->ScaleWidth();
   my $treeview_width = 200;

   $self->TreeView->Move(0, 0);
$self->TreeView->Resize($treeview_width, $windowheight - $self->StatusBar->ScaleHeight);

   $self->TabFrame->Move($treeview_width, 0);
$self->TabFrame->Resize($windowwidth - $treeview_width, $windowheight - $self->StatusBar->ScaleHeight);

   $self->TabFrame->TabPage0->RichEdit1->Move(0, 0);
$self->TabFrame->TabPage0->RichEdit1->Resize($self->TabFrame->TabPage0->ScaleWidth, $self->TabFrame->TabPage0->ScaleHeight);

   $self->TabFrame->TabPage1->RichEdit2->Move(0, 0);
$self->TabFrame->TabPage1->RichEdit2->Resize($self->TabFrame->TabPage0->ScaleWidth, $self->TabFrame->TabPage0->ScaleHeight);

   $self->TabFrame->TabPage2->ListView1->Move(0, 0);
$self->TabFrame->TabPage2->ListView1->Resize($self->TabFrame->TabPage2->ScaleWidth, $self->TabFrame->TabPage2->ScaleHeight - 40 ); $self->TabFrame->TabPage2->Button1->Move($self->TabFrame->TabPage2->ScaleWidth/2 - 70, $self->TabFrame->TabPage2->ScaleHeight - 30); $self->TabFrame->TabPage2->Button2->Move($self->TabFrame->TabPage2->ScaleWidth/2 , $self->TabFrame->TabPage2->ScaleHeight - 30);

   $self->TabFrame->TabPage3->ListView2->Move(0, 0);
$self->TabFrame->TabPage3->ListView2->Resize($self->TabFrame->TabPage3->ScaleWidth, $self->TabFrame->TabPage3->ScaleHeight);

   $self->StatusBar->Move(0, $windowheight - $self->StatusBar->Height);
   $self->StatusBar->Resize($windowwidth, $self->StatusBar->Height);

   return 1;
}
__END__

Have fun,
Rob.


Jez White wrote:

Ok - Ariel's solution (calling DESTROY) on the richedit controls before exit solves the crash for your example:) I must admit I'm a bit stumped at the button hang - I was able to reproduce it. It must be something to do with TabFrame (probably a scoping issue) since if the buttons are created directly on the tab strip they work fine. I'm not to sure what to suggest - other than don't use the TabFrame package:) Cheers, jez.

    ----- Original Message -----
    *From:* [EMAIL PROTECTED]
    <mailto:[EMAIL PROTECTED]>
    *To:* perl-win32-gui-users@lists.sourceforge.net
    <mailto:perl-win32-gui-users@lists.sourceforge.net>
    *Sent:* Tuesday, May 10, 2005 7:52 PM
    *Subject:* [perl-win32-gui-users] Button and RichEdit issues


    Thank you everyone for the replies to my Right Click problems, the
    issue is now solved.  However, I am having a couple more issues
    that I can not figure out how to get around.

    The first issue is a known one, and it concerns a crash with
    RichEdit on 1.0 when exiting the application.  I have created
    Global vars for the RichEdit controls, but I still get a crash on
    exit, so I'm not sure what i'm doing wrong.

    The next issue concerns a couple buttons that I have created and
    when I go to click on them the program just hangs.  I tried adding
    a BusyTimer to make sure that the program was hanging and the
    BusyTimer stopped spewing out information, so I know it hung.

    Anyway, I have included some code below in hopes that someone can
    help me fix these issues.  Please let me know if you need any more
    information and I will gladly include it.

    Thanks.

    Len.


Reply via email to