最近在用Delphi7做项目。C/S结构。一个主要的执行程序,加上动态加载的功能模块。因此对dll做了一个模板,每一个功能模块都从这里继承。现在碰到奇怪的一个问题。
在项目的Options中,如果将Build with runtime packages 打勾,则加载dll功能模块,并打开其中的窗口时不会出错,但是如果这个勾不打,系统加载dll时就会报Access violation at address XXX.dll的错误(这个错误也会出现在这个dll中打开下面测试过的那个父MDIChild窗口时)。
另外,我在模板上直接写了一个测试程序,打开用来继承的父MDIChild窗口(该窗口是标准Delphi组件),不管是不是将Build with runtime packages 打勾都不会报错。
请高手指点。另:用调试跟踪过,错误出现在执行到delphi的graphics单元中的一个过程里,所以不知道该怎么判断哪里不对了。
对于Dll.Application.Handle := Application.Handle我是写过的,所以可以排除这个可能性。

解决方案 »

  1.   

    starluck,详细说说,什么是共享包?这个我还真没注意。
    我把Build with runtime packages打勾以后,系统需要如下包:
    adortl70.bpl、bdertl70.bpl、dbrtl70.bpl、vcl70.bpl、vcldb70.bpl、vcljpg70.bpl、vclsmp70.bpl、vclx70.bpl在程序执行的目录下放这些包就不会出错了。
      

  2.   

    楼上的意思,是不是说把我把Build with runtime packages的勾去掉?
    但去掉这个勾以后,我的程序运行就会出错。
      

  3.   

    我程序中用到的包,没有办法编译到exe或者dll里面又不出错吗?我记得以前写这类似这样结构的程序的时候,没有出现这样的问题,更不用带一大堆bpl的包。
      

  4.   

    LoadPackage 檢查下你代碼裏沒有這個。或者自己程序是EXE+BPL來做的。
      

  5.   

    楼主如果用DLL仅仅是为了模块化程序或者动态加载的话,推荐使用package(包),用Dll的话,会遇到很多郁闷的问题 以下是我收集的一些问题 及其解决办法,供楼主参考 Cannot assign a TFont to a TFont 
    主要是由于在创建Dll的Form时,将外部调用程序的Application、Screen和MainForm都传给了Dll并且将Dll中的这几个对象都替换掉了的缘故,解决的办法是只需将外部调用程序的Application.Handle及MainForm传给Dll并替换掉Dll中的这两项值就可以了,我已测试通过,可以试一试。如:  
      var      
      p:  PLongInt;  
        
      {DllApp:  TApplication  Dll中的Application  
        OutApp:  TApplication  外部调用程序的Application  
      }  
      p:=@(DllApp.MainForm);  
      DllApp.Handle  :=  OutApp.Handle;  
      p^:=LongInt(OutApp.MainForm);  The  DLL  will  not  use  the  same  classes  as  the  main  program  even  when  compiled  from  the  same  source.  Objects  will  look  the  same,  but  their  classes  will  not  compare  equal.  That's  why  assigning  a  TFont  value  from  the  DLL  to  a  TFont  property  in  the  main  program  (or  the  other  way  around)  doesn't  work:  the  Assign  procedure  is  looking  for  the  main  program's  TFont  class,  and  never  recognises  the  DLL's  TFont  object. 最近在公司写一个外壳程序,调用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窗体的ResizeEXE主窗体改变尺寸时,窗体中的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,那个该死的应用程序图标又没了,焦虑中....