• dll 问题 (转)


    最近在公司写一个外壳程序,调用DLL插件把FORM嵌入到EXE中的一个PANEL中,其中遇到了不少的问题,大部分已经解决,还有几个至今没有找到解决方法,有待研究,也希望知道解决方法的富翁共享一下研究成果.

      以下列出的问题及解决方法仅针对我写的程序(DLL插件把FORM嵌入到EXE中的一个PANEL中),和自己的解决方法.

      从遇到的问题看出,DELPHI封装了太多的东西,有时候直接使用API会有意想不到的效果.
      经验:DLL与EXE之间的通讯应该全部使用消息.
    第一个问题:Tab键和Enter键在DLL的FORM中无效
    原始程序:
    //frmDll为DLL中的FORM,frmEXE为EXE主窗体,下同
    //下面的代码为什么直接引用Exe中的Form又引用Dll中的Form?只是为了方便阅读,实际只是传递一个句柄,下同
    //panWorkSpace为Exe中的一个TPanel,DLL中的窗体要嵌入其中
        frmDll.WindowState :=  wsMaximized;
        frmDll.BorderStyle :=  bsNone;
        windows.SetParent(frmDll.Handle,frmExe.panWorkSpace.Handle);

    发现Tab及Enter键在嵌入的FORM中无效,去掉
        frmDll.BorderStyle :=  bsNone;
    后正常,但我不需要标题,就用API解决
        frmDll.WindowState :=  wsMaximized;
        SetWindowLong(frmDll.Handle,GWL_STYLE,GetWindowLong(frmDll.Handle,GWL_STYLE) and not (WS_CAPTION or WS_THICKFRAME));
        windows.SetParent(frmDll.Handle,frmExe.panWorkSpace.Handle);
    其中WS_CAPTION和WS_THICKFRAME分别表示标题栏和边框,问题解决.
     
    第二个问题:DLL窗体的Resize
    EXE主窗体改变尺寸时,窗体中的Panel也会跟着变(Panel.Align设为了alClient),但其中嵌入的DLL窗体不会跟着变,解决方法:
    //exe窗口接收消息并改变子窗体大小
    //FChildWindowList为TList,子窗体的结构信息列表
    Type
      //子窗体一些信息的结构体
      PFormInfo     = ^TFormInfo;
      TFormInfo     = record
        Handle        : HWND;
        Parent        : HWND;
        Style         : HWND;
      end;

      TfrmExe = class(TForm)
      private
        procedure WMSize(var Message:TWMSize);message WM_Size;
      end;

    procedure TfrmExe.WMSize(var Message: TWMSize);
    //ReSize消息
    var
      i   : Integer;
      rc  : TRect;
    begin
      inherited;
      if GetWindowRect(panWorkSpace.Handle,rc) then
        if Assigned(FChildWindowList) then
          for i :=  0 to FChildWindowList.Count - 1 do
            SetWindowPos(PFormInfo(FChildWindowList[i]).Handle, 0,
                0, 0, rc.Right - rc.Left, rc.Bottom - rc.Top,
                SWP_NOACTIVATE);
    end;
     
    第三个问题:焦点在DLL中的窗体时,切换到其它应用程序,再点击任务栏上Application对象的按钮,不能切换过来
    焦点在DLL中的窗体时,切换到其它应用程序, 再点击任务栏上Application对象的按钮,不能切换过来,EXE主窗体不最小化,切换到其它程序,直接点嵌入的DLL窗体,DLL窗体获得焦点, 发现Application对象在任务栏上的按钮是被按下去了,但是EXE窗体并没有被提到最前,还有,DLL窗体得到焦点时,EXE窗体的标题栏变为灰 色,这些都是不符合使用习惯的,虽然不影响使用,但我觉得还是要解决.

    1.DLL窗体得到焦点时,EXE窗体的标题栏变为灰色的解决方法.
    DLL窗体
      TfrmDll=class(TForm)
      private
        procedure WMActivate(var Message : TMessage);message WM_ACTIVATE;
      end;

    procedure TfrmDll.WMActivate(var Message: TMessage);
    begin
      inherited;
      SendMessage(frmEXE.Handle, WM_NCACTIVATE, Integer(True), 0);
    end;

    2.焦点的问题解决方法
    把下面这个单元加入工程
    //==============================================================================
    // Unit Name: AppHandler
    // Author   : ysai
    // Date     : 2003-06-05
    // Purpose  : 处理焦点问题
    // History  :
    //==============================================================================

    unit AppHandler;

    interface

    uses
      Windows, Messages, SysUtils,Forms;

    implementation

    var
      OldWProc      : TFNWndProc;

    function NewWndProc(
        Handle  : HWND;
        Msg     : Integer;
        wParam  : Longint;
        lParam  : Longint
        ):Longint; stdcall;
    begin
      Result  :=  0;
      case Msg of
        WM_ACTIVATEAPP  : //嵌入到主窗口的DLL中的窗口得到焦点不会把程序提前
          begin
            case wParam of
              0 : //应用程序失去焦点
                begin
                  if Assigned(Application.MainForm)
                      and (GetWindowLong(Application.Handle, GWL_EXSTYLE)
                      and WS_EX_TOOLWINDOW = 0) then
                    SendMessage(
                        Application.MainForm.Handle,
                        WM_NCACTIVATE,
                        Integer(False),
                        0);//失去焦点把标题栏变灰
                end;
              1 : //应用程序得到焦点
                begin
                  if Assigned(Application.MainForm)
                      and (GetWindowLong(Application.Handle, GWL_EXSTYLE)
                      and WS_EX_TOOLWINDOW = 0) then
                  SendMessage(
                      Application.MainForm.Handle,
                      WM_ACTIVATE,
                      WA_ACTIVE,
                      1);//注意,这里设为1,后面会用到
                end;  //case wParam
            end;
            Result := CallWindowProc(OldWProc, Handle, Msg, wParam, lParam);
          end;  //msg : WM_ACTIVATEAPP
        else
          Result := CallWindowProc(OldWProc, Handle, Msg, wParam, lParam);
      end;  //case msg
    end;

    initialization
      //取代应用程序的消息处理
      OldWProc    := TFNWndProc(SetWindowLong(Application.Handle, GWL_WNDPROC,
        Longint(@NewWndProc)));

    finalization
      //还原消息处理过程
      if OldWProc <> nil then
        SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(OldWProc));

    end.
    //单元结束

    //EXE程序主窗口
      TfrmEXE = class(TForm)
      private
        procedure WMActivate(var Message : TMessage);message WM_ACTIVATE;
      end;

    procedure TfrmExe.WMActivate(var Message: TMessage);
    //激话消息,Message.lParam=1时是OAAppHandler单元发来的,激活子窗口
    var
      hWindow : HWND;
    begin
      inherited;
      if Message.lParam = 1 then //如果是1就是AppHander发出的消息,将焦点设到活动子窗体
      begin
        hWindow :=  GetActiveChildWindowHandle;//这个函数得到活动子窗体
        //如果有子窗口而且不存在模态显示的窗体则把焦点移到子窗体上
        if (hWindow > 0) and IsWindowEnabled(Application.Handle) then
          windows.SetForegroundWindow(hWindow);
      end;
    end;
     
    第四个问题:SpeedButton在DLL中鼠标离开不会恢复平面(ShowModal时不会出现)(未解决)
    SpeedButton.Flat设为真时,在DLL中鼠标离开不会恢复平面状态,而ShowModal时不会出现,不知道原因,应该是消息处理得不好,不知道有没有人解决过
     
    又一个焦点问题:焦点在DLL窗体时,按Alt+Tab,对话框里出来的程序中竟然没有EXE程序!
    焦点在EXE窗体上时没问题,焦点在DLL窗体上时,用Alt+Tab不会出现EXE应用程序的图标,切换到其它任务后,也不能用Alt+Tab切换回来!这是个比较大的BUG,还未找到原因
    用spy++看了一下,按下Alt+Tab键,窗体收到了一个WM_CANCELMODE消息,我想,既然焦点在exe窗体上时可以看到图标,而在dll上看不到,那么我在收到这个消息时把焦点给设到exe上不就可以了?
      事实证明这点是可行的,代码如下:
      TDllForm = class(TForm)
      private
        procedure WMCancelMode(var Message : TMessage);message WM_CANCELMODE;
      end;

    procedure TDllForm.WMCancelMode(var Message: TMessage);
    //处理Alt+Tab键弹出的对话框中没有应用程序图标问题
    begin
      SetForegroundWindow(exeForm.Handle);  //把exe窗体设为当前有焦点的窗体
    end;

       现在不论焦点在exe的窗体上还是dll的窗体上,按Alt+Tab出现的对话框中都有应用程序的图标,但不同的是,焦点在exe的窗体上时按 Alt+Tab,默认激活的是下一个应用程序,而焦点在dll窗体上时按Alt+Tab,默认激活的是第一个,也就是应用程序本身,实际激活的是exe窗 体.
      虽然还是不怎么习惯,但总算把它给弄出来了,以后有好的解决方法再贴上来.
     
    Hint的问题(未解决)
    焦点在Dll中的窗体时,鼠标移动到控件上不会显示控件的Hint,而且Application.OnHint事件也不会发生,但是焦点在Exe窗体上时,把鼠标放在Dll窗体中的控件上却能显示Hint.原因还未找到:(
     
    ALT+TAB解决了,但是那是键盘,鼠标操作还是有问题
    焦点在DLL中时,用鼠标点其它应用程序,失去焦点了,再按ALT+TAB,那个该死的应用程序图标又没了,焦虑中....
  • 相关阅读:
    公共开发中js代码的管理
    利用kibana插件对Elasticsearch进行映射
    利用kibana插件对Elasticsearch进行批量操作
    利用kibana插件对Elasticsearch进行文档和索引的CRUD操作
    elasticsearch搜索框架的安装相关
    python面试必问 知识整理
    Selenium定位不到元素的解决方法—iframe挡住了去路
    python 几个简单算法详解
    爬虫突破封禁常用方法
    转载:使用Tornado+Redis维护ADSL拨号服务器代理池
  • 原文地址:https://www.cnblogs.com/edrp/p/2437992.html
Copyright © 2020-2023  润新知