求助,回答的都有分。帮忙解决问题的另加200(300分)

  • 主题发起人 主题发起人 Another_eYes
  • 开始时间 开始时间
A

Another_eYes

Unregistered / Unconfirmed
GUEST, unregistred user!
遇到一个很棘手的问题:
我写了个控件,运行时动态create的话一切正常。安装也正常,design
时也能将我的控件放到form上,显示也很正常,用Object Inspector
修改其中的属性时显示也有反应。
编译也通过。但是一旦运行,即出现‘Access Violation at address xxxxx, write of address xxxxxx...', 根本无法启动程序。
单步跟踪发觉application.run不能执行。
我在控件中用了makeobjectinstance, setwindowlong(form.handle,
GWL_WNDPROC,...)将我的一个类中的过程替换了form的wndproc
替换的工作我是override control.SetParent方法,然后在其中进行的。
可以肯定,此时form.handle已经建立.
由于动态create的控件运行正常,可以排除替换form的wndproc的
中代码有问题。
现在怀疑问题出在替换过程中,即makeobjectinstance出错,但是
无法跟踪,不能确定问题所在。

求助:
1. 除了makeobjectinstance之外是否还有其他可能出现该问题?
2. 有无方法跟踪一个已经安装了的控件中的代码。
3. 在我控件create整个过程的哪一步运行makeobjectintance才不会
出现问题?
4. 有没有其他好的建议?
 
不知道我的问题是否和你类似。我完全按照cantu的delphi高级开发指南写的替换
form的wndproc会出错。
unit StaticForm;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
TStaticForm = class(TComponent)
private
{ Private declarations }
OldWndProc,NewWndProc:Pointer;
protected
{ Protected declarations }
function FormHandle:THandle;
procedure NewWndMethod(var Msg:TMessage);
public
{ Public declarations }
constructor Create(AOwner:TComponent);override;
destructor Destroy;override;
published
{ Published declarations }
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('hubdog lib', [TStaticForm]);
end;

{ TStaticForm }

constructor TStaticForm.Create(AOwner: TComponent);
var
I:integer;
begin
for I:=0 to AOwner.ComponentCount-1 do
if AOwner.Components is TStaticForm then
raise Exception.Create('only one instatnce can be put on Form');
inherited;
if (Owner=nil) or not (Aowner is TForm) then
raise Exception.Create('Owner must be a Form');
if not (csDesigning in ComponentState) then
begin
NewWndProc:=MakeObjectInstance(NewWndMethod);
OldWndProc:=Pointer(SetWindowLong(FormHandle,gwl_wndPRoc,Longint(NewWndPRoc)));
end
else
begin
NewWndPRoc:=nil;
OldWndProc:=nil;
end;
end;

destructor TStaticForm.Destroy;
begin
if Assigned(NewWndPRoc) then
begin
//SetWindowLong(FormHandle,gwl_WndPRoc,
//Longint(OldWndProc));//问题在这,把注释去掉,运行时没问题
//退出时就会出现类似你的错误,当时我看了一下rxlib的rxwindowhook源程序,
//它用了类似的方法(更复杂一些,就没有这个问题,好象是又多处理了一些//windows的退出消息等),我没仔细看,我觉得你可以仔细看一下。
FreeObjectInstance(NewWndProc);
end;
inherited;
end;

function TStaticForm.FormHandle: THandle;
begin
Result:=(Owner as TForm).Handle;
end;

procedure TStaticForm.NewWndMethod(var Msg: TMessage);
begin
if Msg.Msg=wm_NCHitTest then
begin
Msg.Result:=CallWindowProc(oldWndProc,
FormHandle,Msg.Msg,Msg.wParam,Msg.lParam);
if Msg.Result=htCaption then
Msg.Result:=htClient;
end
else
Msg.Result:=CallWindowProc(oldWndProc,
FormHandle,Msg.Msg,Msg.wParam,Msg.lParam);
end;

end.
 
在destroy里写freeobjectinstance是会出错. 其实不freeobjectinstance也没
多大问题.需要的话override destroywnd, 在那里free.
我的问题是动态create时正常而在design时放置一个控件然后运行就出错.
两码事.
rxlib里是什么时候替换wndproc的?
 
rxlib的实现是建立了一个tlist的继承类来管理form上的所有TRxwindowshook,
它用allocatehwnd(wndproc)得到了一个窗口句柄,wndproc如下
procedure THookList.WndProc(var Msg: TMessage);
var
Hook: TControlHook;
begin
try
with Msg do begin
if Msg = CM_RECREATEWINDOW then begin
Hook := TControlHook(LParam);
if (Hook <> nil) and (IndexOf(Hook) >= 0) then
Hook.HookControl;
end
else if Msg = CM_DESTROYHOOK then begin
Hook := TControlHook(LParam);
if Assigned(Hook) and (IndexOf(Hook) >= 0) and
(Hook.FList.Count = 0) then Hook.Free;
end
else Result := DefWindowProc(FHandle, Msg, wParam, lParam);
end;
except
Application.HandleException(Self);
end;
end;
然后每当rxwindowshook.wincontrol:=form1时,在Tlist里加上hook,同时调用
ControlHook.hookcontrol.
procedure TControlHook.HookControl;
var
P: Pointer;
begin
if Assigned(FControl) and not ((csDesigning in FControl.ComponentState) or
(csDestroying in FControl.ComponentState) or FDestroying) then
begin
FControl.HandleNeeded;
P := Pointer(GetWindowLong(FControl.Handle, GWL_WNDPROC));
if (P <> FNewWndProc) then begin
FPrevWndProc := P;
SetWindowLong(FControl.Handle, GWL_WNDPROC, LongInt(FNewWndProc));
end;
end;
end;

//若想看全部源程序,不长500多行,可帖上来。
 
免了免了, 问题解决了. 嘿嘿
再等几个分赃吧
 
问题出在那?,学习学习,
 
是不是重新建了个工程就解决了?
 
经验:
自己做的控件如果生成个tform的子类时(推而广之, 所有继承自TWinControl
并且准备设置parent为desktop的类)千万要注意, 不能在控件的create
整个过程中(override create, createwnd, createwindowhandle,
createhandle, setparent等等方法中)
用类似
FMyForm := TForm.Create(self)这种语句. 用的话会引起application搞不清
mainform而出现错误使程序无法运行(鬼知道application是怎么判断mainform
的).
 
一个疑问: 这时候的MainForm应该是已经创建好的, 会不会有其他方面的
原因?
 
看来就是mainform的问题. 我的控件中create了一个form, 本来是在控件的
create中生成该form的, 所以动态生成没有问题, 而作为控件放在form上
运行就出错. 后来我将我控件的create中这句改成 xxxform := nil;
而在用到时if not assigned(xxxform) then xxxform := tform.create(application)
结果就一点错都没有了. 替换wndproc也很正常.
 
呵呵, 是不是design的问题:-) 如果build以后, 关闭delphi, 单独运行exe就会
没有问题了:-)
 
如果如你所说, 那重载构件的Loaded, 在这里创建你的Form岂不更好?
 
to cytown. 不是, 都有问题.
to cmxu. 不知道.
有谁能告诉我application.run及form create与load的顺序?
 
Constructor Create > procedure Loaded > procedure OnFormCreate >
Application.Run (不敢十分肯定)
 
类似问题我也遇到过.

"有谁能告诉我application.run及form create与load的顺序?"

好像form Create在前,application.run里面 form load.



 
不光是form, 还有form上所有control的顺序呢?
 
Form Constructor>Control Constructor>Form loaded>Control Loaded>Form Event
 
多人接受答案了。
 
后退
顶部