Delphi Dll 消息处理

  转载:http://blog.csdn.net/lailai186/article/details/8770643

  事情的导火线是GIF图片的显示. 在应用程序中, 利用三方的GIFImage.pas可以很好的显示GIF图片.
这次, 要在一个DLL中显示一个GIF图片. 还是像往常一样拖个TImage放到窗体上, 打开一个动态GIF图片. 编译, 运行.

怪了: GIF图片显示是静态的. 还以为Delphi又出Bug了. 于是又把图片放到程序主窗体上一运行, 动的. 这下头可大了!

找相关的资料, 没有.

看来事情还得自己解决: 于是专心研究起GIFImage.pas, 首先看的当然是重画部分的代码了( 呵呵, 这是我一贯的风格: 觉得是哪里出问题就先看哪里, 不管是谁的代码 ). 经过一番搜索.

把目标定位在线程上. GIFImage.pas的重画其实就是调用一个线程, 在线程内读取文件中相应的图像数据画到目标位置.

在线程内重画是调用线程的Synchronize过程. 以前知道这个过程是为了避免多个线程同时访问同一个数据或对象的. 现在得对它的执行方法做一番了解才行.

经过一翻摸索, 找到了解决方法. 在DLL的窗体上放一个TTimer控件. Interval尽量小. OnTimer只添加一行代码: CheckSynchronize;

运行. OK. 图片动起来了......( 这种方法所存的问题就不用再多说了吧. )

但接下来的一个问题却很恼火的: 在DLL的窗体上放一个TSpeedButton控件, Flat属性设置为True. 运行. 当鼠标从TSpeedButton上移过时, TSpeedButton怎么也还原不了. 试着调用它的重画等功能. 全部没用. 好几天的时间一直在思考这个问题.

后来在处理应用程序的消息的时候, 突然想到: DLL虽然有自己Application, 但它并没有自己的消息循环, 而线程的Synchronize不能执行, TSpeedButton不能还原都是因为有些消息没有得到相应的处理而导致的.

也就是说, 只要给DLL加上一个消息循环, 上面的这些问题都会全部解决.

刚开始的时候想从主程序发送消息给DLL. 可消息截取的结果是: 很多DLL里产生的消息并没有发送给主程序. 看来这个方法是行不通的. 只得另寻方法.

在看到以下几行大家很熟悉的代码后想到.
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
可不可以给DLL也加上这们的代码呢?

动手实验, 创建一个DLL, DLL里包含一个窗体DLLForm. 从DLL里导出一个函数. 加上上面的代码. 如下:
procedure InitDLL; stdcall;
begin
  Application.Initialize;
  Application.CreateForm(TDLLForm, DLLForm);
  Application.Run;
end;
再到主程序窗体的创建事件代码如下:
procedure TForm1.FormCreate(Sender: TObject);
begin
  InitDLL;
end;
运行, 结果不对. 它是先打开主窗体了... :( 郁闷. 并且 InitDLL; 也不是立即返回, 而是当DLL里主窗体关闭后才返回. 其实早就应该想到了.
把OnCreate的代码放到一个TTimer控件里. Interval为1. OnTimer的代码如下.
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  TTimer(Sender).Enabled := False;
  InitDLL;
end;
这下可以了. 但不能让DLL里的窗体一开始就显示出来吧. 得. 再改改InitDLL. 如下:
procedure InitDLL; stdcall;
begin
  Application.Initialize;
  Application.ShowMainForm := False;
  Application.CreateForm(TDLLForm, DLLForm);
  Application.Run;
end;
主窗体不显示了, 得加上一个, 看看效果:)
再到DLL里加上一个Form( 命名为 DLLChildForm ), 在窗体上放一个TSpeedButton控件.
再给DLL导出一个函数, 如下:
procedure CreateChildForm; stdcall;
begin
  with TDLLChildForm.Create(Application) do
  begin
    Show;
  end;
end;
再到主窗体中添加一个按钮. 点击事件代码如下.
procedure TForm1.Button1Click(Sender: TObject);
begin
  CreateChildForm;
end;
运行. 结果理想: TSpeedButton在鼠标移过后能还原了. 呵呵...... 真爽!

不过, 问题又来了. 程序退出时报异常了. 想一下, 哦. DLL里的窗体资源还没有释放呢. 得, 再从DLL里导出一个过程, 代码如下:

  1. procedure DestoryDLL; stdcall;
  2. var
  3. i: Integer;
  4. begin
  5. for i := Application.ComponentCount - 1 downto 0 do
  6. begin
  7. if Application.Components[i].ClassNameIs(‘TDLLChildForm‘) then
  8. begin
  9. TDLLChildForm(Application.Components[i]).Release;
  10. end;
  11. end;
  12. if DLLForm = nil then
  13. begin
  14. Exit;
  15. end;
  16. DLLForm.Release;
  17. DLLForm := nil;
  18. end;

再给主程序主窗体的OnCloseQuery添加代码如下:
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  DestoryDLL;
end;
运行. 靠, 虽然DLL里的窗体全关闭了, 可主程序还是退不出啊. 换换方法, 把 DLLForm.Release; 这里改成Application.Terminate; 试试. 还是不行. 咋回事?
反复调试, 发现虽然Terminate了, 可Run仍在循环. 并没有结束.
再研究Run的代码. 呵呵. 有了.
把Application.Terminate;换成PostMessage(Application.Handle, WM_QUIT, 0, 0);
运行, 还是不行. 但Run是循环是退出了. 那哪里还会有问题呢? 该不会是窗体没有释放吧. 好, 在PostMessage前加上DLLForm.Release;这时, DestoryDLL过程的代码如下:

  1. procedure DestoryDLL; stdcall;
  2. var
  3. i: Integer;
  4. begin
  5. for i := Application.ComponentCount - 1 downto 0 do
  6. begin
  7. if Application.Components[i].ClassNameIs(‘TDLLChildForm‘) then
  8. begin
  9. TDLLChildForm(Application.Components[i]).Release;
  10. end;
  11. end;
  12. if DLLForm = nil then
  13. begin
  14. Exit;
  15. end;
  16. DLLForm.Release;
  17. //  Application.Terminate;
  18. PostMessage(Application.Handle, WM_QUIT, 0, 0);
  19. DLLForm := nil;
  20. end;

运行. OK. 完美解决...
再加上线程试试( 这时InitDLL过程要改成如下, 这样才能真正的处理所有的消息 ) . 真爽. 与想像的一样.

  1. procedure InitDLL(AHandle: Thandle); stdcall;
  2. begin
  3. Application.Initialize;
  4. Application.ShowMainForm := False;
  5. Application.CreateForm(TDLLForm, DLLForm);
  6. // 保存原来的句柄
  7. DLLForm.Tag := Application.Handle;
  8. // DLL 从属的句柄 ( 如果没有此行, 线程的执行不能达到理想效果 )
  9. // 并且这样才能真正的让消息循环处理它应处理的所有消息
  10. Application.Handle := AHandle;
  11. Application.Run;
  12. Application.Handle := DLLForm.Tag;
  13. end;
时间: 2024-08-04 01:36:43

Delphi Dll 消息处理的相关文章

在.net中调用Delphi dll的Pchar转换

Pchar是非托管代码,要在.net中调用Delphi dll中的功能,请使用MarshalAs属性告知.net调用PInvoke去转换.net中标准的string类型.如果Delphi dll是Delphi2007以前的版本编译的,则Pchar是Ansi类型:如果Delphi dll是Delphi2009以后的版本编译的,则Pchar是Unicode类型.请看以下实例. Ansi类型: [DllImport("my.dll")] [return: MarshalAs(Unmanage

Delphi Dll 动态调用例子(3)-仔细看一下

http://blog.163.com/bxf_0011/blog/static/35420330200952075114318/ Delphi 动态链接库的动态和静态调用 为了让人能快速的理解 静态调用.动态调用,现在做一个函数封装在一个DLL中,然后在APPLICATION form里面调用这个函数,这个函数处理两个数的和.用代码和图片说话:代码如下 library Project1; { Important note about DLL memory management: ShareMe

Delphi DLL制作和加载 Static, Dynamic, Delayed 以及 Shared-Memory Manager

一 Dll的制作一般分为以下几步:1 在一个DLL工程里写一个过程或函数2 写一个Exports关键字,在其下写过程的名称.不用写参数和调用后缀.二 参数传递1 参数类型最好与window C++的参数类型一致.不要用DELPHI的数据类型.2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态.成功或失败的返回值最好为1[成功]或0[失败].一句话,与windows c++兼容.3 用stdcall声明后缀.4 最好大小写敏感.5 无须用far调用后缀,那只是为了与windows 1

Delphi Dll 动态调用例子(2)

http://zhidao.baidu.com/question/157196792.html delphi动态调用DLL 写了个1.dll内容如下 library Project2; uses SysUtils, Classes; {$R *.res} function abc(x,y:Integer):Integer;stdcall; begin Result:=x+y; end; exports abc; end. 如何动态调用它呢. 比如想实现Edit3.Text:=IntToStr(a

Borland.Delphi.dll

Borland Delphi Runtime for .NET Imports Borland.DelphiImports Borland.Delphi.UnitsImports Borland.Vcl Borland.Delphi.Units.System.AnsiEncoding.GetBytes Borland.Vcl.Units.SysUtils.InitPlatformId Borland.Vcl.Units.SysUtils.InitSysLocale()Borland.Vcl.Un

Delphi Dll 动态调用例子(1)

http://blog.sina.com.cn/s/blog_62c46c3701010q7h.html 一.编写dll library TestDllByD2007; uses  SysUtils,  Classes;  function test(const astr:PChar):Boolean;stdcall;  begin    Result:=True;  end;{$R *.res}  exports test;beginend. 注意:1.不能使用string类型,否则涉及到资源

Delphi dll 断点调试

1.dll 要有一个依托的exe(怎么做 相信用dll了一定知道) 2.选项中的compling中的debugging中的选项,linking中的所有选项 3.最后一个也就是最重要的 run中的parameters中的 host application选中依托的exe程序 注:有时设好了设断点还是不执行 再重新做3 这样就不老是showmessage了

Delphi Dll示例

//MyInt.pas unit MyInt; interface {$IFNDEF MYLIB} function MyAdd(a,b:integer):integer ;stdcall; {$ENDIF} implementation {$IFNDEF MYLIB} function MyAdd; external 'MyLib.dll' name 'MyAdd'; {$ENDIF} end. //MyLib.dpr library MyLib; { Important note about

Delphi dll的使用

DLL定义 library SUM; { Important note about DLL memory management: ShareMem must be the first unit in your library's USES clause AND your project's (select Project-View Source) USES clause if your DLL exports any procedures or functions that pass strin