您的位置:首页 > 编程语言

DLL内线程同步主线程研究(子线程代码放到主线程执行)

2010-07-29 00:52 190 查看
DLL内线程同步主线程研究(子线程代码放到主线程执行)

我们在实际项目中经常会用到多线程编程,比如Socket编程等,在创建的线程内同步主线程一般使用Synchronize方法实现子线程操作放到主线程执行,Synchronize使用非常方便,且在2009及以上版本都可以使用匿名方法,这样给我们多线程带来了很大的便利。但是实践证明Synchronize只在主程序内正常工作。如果在主程序加载的DLL程序内运行使用Synchronize方法要求的条件比较苛刻,它要求必须把DLL程序拷挂到主程序,同时DLL内有窗体状态需为Modal或者主程序内窗体无一显示。具体见下:

主程序:

unit MainFrm;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TMainForm = class(TForm)

btn1: TButton;

procedure btn1Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.btn1Click(Sender: TObject);

var

FHandle:THandle;

OpenDLLWindow:procedure(AMainHandle:Integer);stdcall;

begin

FHandle:=LoadLibrary(PChar('DLLPrj.dll'));

if FHandle>0 then

begin

OpenDLLWindow:= GetProcAddress(FHandle,PChar('OpenDLLWindow'));

if Assigned(OpenDLLWindow) then

begin

OpenDLLWindow(Application.Handle);

end;

end

else

ShowMessage('加载DLL失败!');

end;

end.

DLL程序:

unit DLLFrm;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs;

type

TDLLForm = class(TForm)

procedure FormShow(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

type

TDLLThread = class(TThread)

protected

procedure Execute; override;

end;

var

DLLForm: TDLLForm;

implementation

{$R *.dfm}

procedure TDLLThread.Execute;

var

Count:Integer;

tp:TThreadProcedure;

begin

Count:=0;

while True do

begin

try

Synchronize(

procedure

begin

ShowMessage(IntToStr(Count));

Count:=Count+2000;

end

);

except

// ignore error

end;

Sleep(2000);// 暂停两秒

end;

end;

procedure OpenDLLWindow(AMainHandle:Integer);stdcall;

begin

if DLLForm=nil then

DLLForm:=TDLLForm.Create(Application);

// 拷挂到主程序

Application.Handle:=AMainHandle;

DLLForm.ShowModal;

//DLLForm.Show;

end;

exports OpenDLLWindow;

procedure TDLLForm.FormShow(Sender: TObject);

var

DLLThread :TDLLThread;

begin

DLLThread:=TDLLThread.Create(False);

end;

end.

以上代码使用了Synchronize方法进行同步主线程,可以正常运行。但如果我们把DLL项目内方法OpenDLLWindow内去掉Application.Handle:=AMainHandle;或者把代码DLLForm.ShowModal;改为DLLForm.Show;这样线程同步主线程时将被阻塞。

这样看来如果如果在DLL项目内如果被主程序加载后窗体显示方式不是Modal的话我们将无法使用便利的Synchronize方法。这样我们使用SendMessage往主线程发送消息成为必须,如我们把DLL工程内代码更改如下:

unit DLLFrm;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs;

type

TDLLForm = class(TForm)

procedure FormShow(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

procedure WMRefreshForm(var Msg: TMessage); message WM_USER+100;

end;

type

TDLLThread = class(TThread)

protected

procedure Execute; override;

end;

var

DLLForm: TDLLForm;

implementation

{$R *.dfm}

procedure TDLLThread.Execute;

var

Count:Integer;

tp:TThreadProcedure;

begin

Count:=0;

while True do

begin

try

SendMessage(DLLForm.Handle, WM_USER+100, Count, 0);

Count:=Count+2000;

except

// ignore error

end;

Sleep(2000);

end;

end;

procedure OpenDLLWindow(AMainHandle:Integer);stdcall;

begin

if DLLForm=nil then

DLLForm:=TDLLForm.Create(Application);

// 拷挂到主程序

//Application.Handle:=AMainHandle;

//DLLForm.ShowModal;

DLLForm.Show;

end;

exports OpenDLLWindow;

procedure TDLLForm.FormShow(Sender: TObject);

var

DLLThread :TDLLThread;

begin

DLLThread:=TDLLThread.Create(False);

end;

procedure TDLLForm.WMRefreshForm(var Msg: TMessage);

begin

if Msg.Msg=WM_USER+100 then

begin

ShowMessage(IntToStr(Msg.WParam));

end;

end;

end.

这样我们的DLL程序就工作正常了,这也是目前的常用方法。但是如果我们在线程内有频繁的同步操作,或者这些同步操作会用到比较多的线程内变量,这样SendMessage就显得麻烦又吃力。如果我们在线程执行方法内能自定义匿名方法就像使用Synchronize那样的话我们的代码量将大大减少,且编程过程将大大简化,这样我们就想到了把匿名方法的指针作为SendMessage的一个参数传到自定义的消息内,然后在自定义消息内执行这个匿名方法。令我们庆幸的是这样是行的通的。如我们把DLL工程内代码修改如下:

unit DLLFrm;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs;

type

TDLLForm = class(TForm)

procedure FormShow(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

procedure WMRefreshForm(var Msg: TMessage); message WM_USER+100;

end;

type

TDLLThread = class(TThread)

protected

procedure Execute; override;

end;

var

DLLForm: TDLLForm;

implementation

{$R *.dfm}

procedure TDLLThread.Execute;

var

Count:Integer;

tp:TThreadProcedure;

begin

Count:=0;

while True do

begin

try

SendMessage(DLLForm.Handle, WM_USER+100,Integer(

@procedure

begin

ShowMessage(IntToStr(Count));

Count:=Count+2000;

end),0

);

// tp:= procedure

// begin

// ShowMessage(IntToStr(Count));

// Count:=Count+2000;

// end;

// SendMessage(DLLForm.Handle, WM_USER+100,Integer(@tp),0);

except

// ignore error

end;

Sleep(2000);

end;

end;

procedure OpenDLLWindow(AMainHandle:Integer);stdcall;

begin

if DLLForm=nil then

DLLForm:=TDLLForm.Create(Application);

// 拷挂到主程序

//Application.Handle:=AMainHandle;

//DLLForm.ShowModal;

DLLForm.Show;

end;

exports OpenDLLWindow;

procedure TDLLForm.FormShow(Sender: TObject);

var

DLLThread :TDLLThread;

begin

DLLThread:=TDLLThread.Create(False);

end;

procedure TDLLForm.WMRefreshForm(var Msg: TMessage);

begin

if Msg.Msg=WM_USER+100 then

begin

TThreadProcedure(Pointer(Msg.WParam)).Invoke;

end;

end;

end.

如果方法TDLLThread.Execute改为

procedure TDLLThread.Execute;

var

Count:Integer;

tp:TThreadProcedure;

begin

Count:=0;

while True do

begin

try

tp:= procedure

begin

ShowMessage(IntToStr(Count));

Count:=Count+2000;

end;

SendMessage(DLLForm.Handle, WM_USER+100,Integer(@tp),0);

except

// ignore error

end;

Sleep(2000);

end;

end;

那么TDLLForm.WMRefreshForm方法需改为:

procedure TDLLForm.WMRefreshForm(var Msg: TMessage);

begin

if Msg.Msg=WM_USER+100 then

begin

TThreadProcedure(Pointer(Msg.WParam)^).Invoke;

end;

end;

这点需要注意。

这样一来我们在DLL程序内只需要稍加修改就可以实现Synchronize一模一样的效果,尽情使用匿名方法给我们带来的方便,以上代码在Delphi2010内通过调试,分享给大家,不足之处往指教。

作者:张皓

2010-7-30
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐