楼上说的我都做过,但都是不能解决问题。最后我采取了一个办法就是在dll的主窗体里面 我重载了wndproc消息处理方法,加入了一个函数 CheckSynchronize(这个函数是通过分析D6的Theard线程类找到的,它的作用是检查当前访问VCL组件的线程是否已经结束)这个函数加入后,线程可以结束并被DLL窗体捕获,但是新问题又产生了,就是在产生一个新线程以后,如果这个时候你什么都不做,鼠标、键盘都不在DLL主窗体中做任何操作,那么这个线程还是不会引发完成事件,wndproc好像没有起作用。但只要你的鼠标或者键盘在DLL主窗体中动一下,产生一个窗口消息、wndproc好像开始工作,进入消息循环,线程结束事件引发。 程序单步执行的话没有上述问题。恳请高手指点,不甚感激!附:wndproc procedure TfrmMp3Tag.WndProc(var Msg: TMessage); var // IconID:integer; pt:TPOINT; begin if msg.Msg = WM_TRAYNOTIFY then begin //在通知消息中,wParam参数为图标的uID,lParam参数为鼠标事件的类型。 // IconID:= msg.WParam; //获取鼠标的在屏幕上的位置 GetCursorPos(pt); //通知消息的处理的基本框架结构如下: case msg.lParam of WM_LBUTTONDOWN: begin //鼠标左键被按下 //ShowMessage('Test!'); end; WM_RBUTTONDOWN: begin //鼠右键被按下 PopTray.Popup(pt.X,pt.Y); end; WM_LBUTTONUP: begin //释放鼠标左键 end; WM_RBUTTONUP: begin //释放鼠标右键 end; WM_MOUSEMOVE: begin //鼠标在图标上移动 end; WM_LBUTTONDBLCLK: begin //鼠标左键双击 frmMp3Tag.show; end; WM_RBUTTONDBLCLK: begin //鼠标右键双击 end; end; //end case end else begin CheckSynchronize;//注意本函数 inherited; end; end;
begin
OnTerminate := Form1.OnThreadExit;
inherited Create(CreateSuspened);
end;如果你的CreateSuspended是False,将那事件写在构造函数中,如果是True,那可以写在
FThread := TMyThread.Create(True);
FThread.OnTerminate := OnThreadExit;
FThread.Resume;
我重载了wndproc消息处理方法,加入了一个函数 CheckSynchronize(这个函数是通过分析D6的Theard线程类找到的,它的作用是检查当前访问VCL组件的线程是否已经结束)这个函数加入后,线程可以结束并被DLL窗体捕获,但是新问题又产生了,就是在产生一个新线程以后,如果这个时候你什么都不做,鼠标、键盘都不在DLL主窗体中做任何操作,那么这个线程还是不会引发完成事件,wndproc好像没有起作用。但只要你的鼠标或者键盘在DLL主窗体中动一下,产生一个窗口消息、wndproc好像开始工作,进入消息循环,线程结束事件引发。
程序单步执行的话没有上述问题。恳请高手指点,不甚感激!附:wndproc
procedure TfrmMp3Tag.WndProc(var Msg: TMessage);
var
// IconID:integer;
pt:TPOINT;
begin if msg.Msg = WM_TRAYNOTIFY then
begin
//在通知消息中,wParam参数为图标的uID,lParam参数为鼠标事件的类型。
// IconID:= msg.WParam;
//获取鼠标的在屏幕上的位置
GetCursorPos(pt);
//通知消息的处理的基本框架结构如下:
case msg.lParam of
WM_LBUTTONDOWN:
begin
//鼠标左键被按下
//ShowMessage('Test!');
end;
WM_RBUTTONDOWN:
begin
//鼠右键被按下
PopTray.Popup(pt.X,pt.Y);
end;
WM_LBUTTONUP:
begin
//释放鼠标左键
end;
WM_RBUTTONUP:
begin
//释放鼠标右键
end;
WM_MOUSEMOVE:
begin
//鼠标在图标上移动
end;
WM_LBUTTONDBLCLK:
begin
//鼠标左键双击
frmMp3Tag.show;
end;
WM_RBUTTONDBLCLK:
begin
//鼠标右键双击
end;
end; //end case
end
else
begin
CheckSynchronize;//注意本函数
inherited;
end;
end;
WndProc只是处理有消息,没消息当然不会处理,如果你想要处理线程的Synchronize的东西,给Application发一个WM_NULL(Delphi6)消息就行了。
WakeMainThread: TNodifyEvent = nil;在D6 Thread中使用Synchronize方法,因为它是调用WakeMainThread方法来实现,而在普通的Application中WakeMainThread是被赋值了的,对应是Application.WakeMainThread, 而当你如果你是写DLL, Form, 虽然它有Application,但是它不同于你的调用的Application,我试了一下,将一个普通Application,调用一个DLL中的Application,它们的Handle和Address是不相同的,
也就是说在DLL中如果你调用线程中的Synchronize方法,那么它会失败,甚至于会引起错误或一直阻塞线程,原因就是DLL中Classes.WakeMainThread没有给赋值,然后线程却一直在阻塞进程的运行(wait for),
情况差不多是这样:
procedure TMyThread.Execute;
begin
...
Synchronize(SomeThing);
...
end;procedure TThread.Synchronize(Method: TThreadMethod);
var
SyncProc: TSyncProc;
begin
SyncProc.Signal := CreateEvent(nil, True, False, nil);
...
FMethod := Method;
SyncProc.Thread := Self;
SyncList.Add(@SyncProc);
if Assigned(WakeMainThread) then //DLL中它是没有值的。一直执行到waitfor
WakeMainThread(Self);//如是普通Application,那么它会去调用CheckSynchronize,将SyncProc.Signal进行置位,那下面的WaitFor函数会马上返回
LeaveCriticalSection(ThreadLock);
try
WaitForSingleObject(SyncProc.Signal, INFINITE); //好了,阻塞进程了,你这线程完了,INFINITE是无限长时间,这线程一直等到天亮SyncProc.Signal只有处理了CheckSynchronize才会被置位。哈哈。
finally
EnterCriticalSection(ThreadLock);
end;
...
end;所以如果想在你的DLL中进行VCL组件同步,你必须写自己的WakeMainThread过程,像Application那样。
constructor TDLLForm.Create(AOwner: TComponent);
begin
Classes.WakeMainThread := WakeMainThread;
inherited Create(AOwner);
end;TDLLForm.WndProc(var Msg: TMessage);
begin
case Msg.Msg of
...
WM_NULL: CheckSynchronize;
end;
end;procedure TDLLForm.WakeMainThread(Sender: TObject);
begin
PostMessage(Handle, WM_NULL, 0, 0);
//or直接调用同步。那在WndProc就不用再处理WM_NULL消息了。
//CheckSynchronize;
end;因为在普通Application中还有一个原因它会调用CheckSynchronize,就是当PeekMessage不了消息时(没有消息时,空闲时候),也会调用它,这一步就不好做了,你的DLLForm是不会去PeekMessage,只有你的线程才会去PeekMessage,而线程的PeekMessage只是对自己线程消息(GetCurrenThreadID)有效,你可以试试在DLLForm中加一个TTimer,定时给自己发消息,像作Idle事件处理.