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.