MDI+DLL的问题说过很多很多了!
就我就发过好多帖子了!
可是一直有问题没有完全解决!首先说一下我的“机制”:
library testDlls002;uses
  SysUtils,
  Classes,
  Windows,
  Forms,
  Unt_Child1 in 'Unt_Child1.pas' {FrmChild001};{$R *.RES}var
  DLLApp: TApplication;
  DLLScr: TScreen;procedure DLLEntryPoint(dwReason: DWORD);
begin
  case dwReason of
    DLL_PROCESS_ATTACH:
    begin
      DLLScr := Screen;
      DLLApp := Application;
    end;
    DLL_PROCESS_DETACH:
    begin
      Screen := DllScr;
      Application := DllApp;
    end;
  end;
end;//// 运行DLL主窗口 //////////////////////////////////////
procedure RunApp(AApplication: TApplication; AScreen: TScreen); stdcall;
begin
  Screen := AScreen;
  Application := AApplication;
  FrmChild001 := TFrmChild001.Create(Application);
end;//////////////////////////////////////////////////////////////////////
exports
  RunApp;//////////////////////////////////////////////////////////////
begin
  DLLProc := @DLLEntryPoint;         //Assign the address of DLLEntryPoint to DLLProc
  DLLEntryPoint(DLL_PROCESS_ATTACH); //Indicate that the DLL is attaching to the process
end.传递了Application何Screen

解决方案 »

  1.   

    这样做一般应用都没问题!
    可是用得多了,用的复杂了就出现问题了!
    如:
    调用DLL中的MDIChild时,在MDIChild中无法使用Tab键、设置的快捷键无效、ShowHint出现“can not assign tfont to tfont”错误以及没有Application.MainForm.ActiveMDIChild
    等等一系列的问题!当然有“解决”办法,就是在所有模块(包括主程序和DLLMDI)都要在Build的选项中,Package的Runtime Packages中勾选Build with runtime packages!
    这样就没问题了!可是为什么呢?!我现在不想这么做——也不能这么做!
    怎么找出问题根源——改进构架?!欢迎讨论!
      

  2.   

    主窗体fsMDIForm
    unit MainUnit1;interfaceuses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      StdCtrls, ToolWin, ComCtrls, Menus;type
      TMainForm = class(TForm)
        MainMenu1: TMainMenu;
        Start: TMenuItem;
        procedure StartClick(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;   T_ProvaChild = procedure (ParentApplication: TApplication; ParentForm: TForm); stdcall;var
      MainForm: TMainForm;implementation{$R *.DFM}procedure TMainForm.StartClick(Sender: TObject);
    var
       DllHandle: THandle;
       ProcAddr: FarProc;
       ProvaChild: T_ProvaChild;
    begin   
       DllHandle := LoadLibrary('ProjectDll');
       ProcAddr := GetProcAddress(DllHandle, 'ProvaChild');
       if ProcAddr <> nil then
       begin
          ProvaChild := ProcAddr;
          ProvaChild(Application,Self);
       end;
    end;end.
    子窗体dll
    library ProjectDll;
    uses
      Windows,
      Messages,
      SysUtils,
      Classes,
      Graphics,
      Controls,
      Forms,
      Dialogs,
      UnitDll in 'UnitDll.pas' {Form1};procedure ProvaChild(ParentApplication: TApplication; ParentForm: TForm); export; stdcall;
    var
      Form1: TForm1;
      DllProc: Pointer;             { Called whenever DLL entry point is called }begin
       Application:=ParentApplication;   Form1:=TForm1.Create(ParentForm);
       Form1.MyParentForm:=ParentForm;
       Form1.MyParentApplication:=ParentApplication;
    //   windows.SetParent(Form1.Handle,ParentForm.Handle);
    //   Form1.FormStyle:=fsMDIChild;
       Form1.Show;
    end;procedure DLLUnloadProc(Reason: Integer); register;
    begin
      if Reason = DLL_PROCESS_DETACH then  Application:=DllApplication;
    end;exports
       ProvaChild;begin
       DllApplication:=Application;
       DLLProc := @DLLUnloadProc;
    end.
    unit UnitDll;interfaceuses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;type
      TForm1 = class(TForm)
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure FormDestroy(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
        MyParentForm: TForm;
        MyParentApplication: TApplication;
      end;var
       DllApplication: TApplication;implementation{$R *.DFM}procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
       Action:=caFree;
    end;procedure TForm1.FormDestroy(Sender: TObject);
    begin
    //   Application:=DllApplication;
    end;end.
      

  3.   

    是呀是呀!
    我也遇到这样的问题!Font的事还好说,在Graphic.pas有一段代码:
    procedure TFont.Assign(Source: TPersistent);
    begin
      if Source is TFont then
      begin
        Lock;
        try
          TFont(Source).Lock;
          try
            FontManager.AssignResource(Self, TFont(Source).FResource);
            Color := TFont(Source).Color;
            if PixelsPerInch <> TFont(Source).PixelsPerInch then
              Size := TFont(Source).Size;
          finally
            TFont(Source).Unlock;
          end;
        finally
          Unlock;
        end;
        Exit;
      end;
      //inherited Assign(Source); //我把这里注释了!
    end;理由:TFont.Assign(Source: TPersistent);中的Source肯定应该是TFont才能Assign!
    所以  
    if Source is TFont then
    begin
      ......
      Exit;
    end;
    干吗还要inherited Assign(Source); 呢?
    :)
    我就注释了!这样就没有TFont的那个错误了!嘻嘻~
    其他的...
    唉~我也不会!
    学习!
      

  4.   

    哦~忘了说了一点
    就是工程的选项的Search Path中要加上$(DELPHI)\Source\Vcl
      

  5.   

    楼上,这样做大家都知道,但是可以解决tab键,快捷键的问题吗
      

  6.   

    是呀~
    解决Font的方法看来挺多的!
    vivian_y(玮玮) 的方法只能解决font!
    theone_jxm()的方法好像也只是这样!?:(
      

  7.   


    我觉得问题主要是出在 Application.MainForm.ActiveMDIChild 上!
    可是在往下就没思路了!
    各位...
    :(
      

  8.   

    从来没有做过MDI程序,更不要说把MDI包含到DLL中了,不过单独窗体或者多窗体包含到DLL中倒是做过N次了
      

  9.   

    //// 运行DLL主窗口 //////////////////////////////////////
    procedure RunApp(AApplication: TApplication; AScreen: TScreen;
      AHintWindowClass: THintWindowClass); stdcall;
    如果传递的参数再多加一个AHintWindowClass: THintWindowClass,ShowHint的问题也可以解决了!而且不用修改Option选项和VCL单元从这里是不是可以说明,问题出在对Form对象的传递上?!
    因为,在Forms.pas这个单元中可以看到
    { Global objects }var
      Application: TApplication;
      Screen: TScreen;
      Ctl3DBtnWndProc: Pointer = nil;  { obsolete }
      Ctl3DDlgFramePaint: function(Window: HWnd; Msg, wParam, lParam: Longint): Longint stdcall = nil; { obsolete }
      Ctl3DCtlColorEx: function(Window: HWnd; Msg, wParam, lParam: Longint): Longint stdcall = nil; { obsolete }
      HintWindowClass: THintWindowClass = THintWindow;
    ......
    有这么多Global objects,
    而在传递Application时,可以构建MDIDll的结构
    在传递Screen后,可以增加Screen的Form都数量,从而增加了MDIChildCount!
    在传递HintWindowClass后,又解决了ShowHint的问题!那再往下是不是就可以逐步解决问题呢?!
    可是
      Ctl3DBtnWndProc: Pointer = nil;  { obsolete }
      Ctl3DDlgFramePaint: function(Window: HWnd; Msg, wParam, lParam: Longint): Longint stdcall = nil; { obsolete }
      Ctl3DCtlColorEx: function(Window: HWnd; Msg, wParam, lParam: Longint): Longint stdcall = nil; { obsolete }
    这是什么呢?看上去好像不对路!?
    怎么搞?就像 vivian_y(玮玮) 说的 “觉得问题主要是出在Application.MainForm.ActiveMDIChild上”,可是在单元Forms.pas中GetActiveMDIChild是这样子的:
    function TCustomForm.GetActiveMDIChild: TForm;
    begin
      Result := nil;
      if (FormStyle = fsMDIForm) and (FClientHandle <> 0) then
        Result := TForm(FindControl(SendMessage(FClientHandle, WM_MDIGETACTIVE, 0,
          0)));
    end;
    总不能又去修改VCL源代码吧?!——这不是“正路”
    怎么再多传递些信息或者其他方法来解决问题呢?!
      

  10.   

    我以前也做过这方面的东西基本思路是和楼主的一样,不过在处理TAB键的时候我是放到子窗口里的KeyUp事件里去处理的。
      

  11.   

    这是我的代码library VendorDll;
    {$DEFINE VENDORLL}uses
      ShareMem,
      SysUtils,
      windows,
      dialogs,
      forms,
      Classes,
      controls,
      vendorInt in 'vendorInt.pas',
      rollpagebuttonsfrm in '..\pubmodule\rollpagebuttonsfrm.pas' {frmRollPageButtons: TFrame},
      postmoneyfrm in 'postmoneyfrm.pas' {frmpostmoney},
      vendorfrm in 'vendorfrm.pas' {frmvendor},
      VledgeQueryfrm in 'VledgeQueryfrm.pas' {frmVLedgeQuery},
      SysfuncInt in '..\sysfuncDLL\SysfuncInt.pas',
      sysobj in '..\sysobject\sysobj.pas',
      VendorListRep in 'VendorListRep.pas' {repVendorList: TQuickRep},
      vlstatrep in 'vlstatrep.pas' {repVLStat: TQuickRep},
      vlstatfrm in 'vlstatfrm.pas' {frmVLStat},
      VendorAdjustfrm in 'VendorAdjustfrm.pas' {frmVendorAdjust},
      registInt in '..\regist\registInt.pas',
      vendorlistfrm in 'vendorlistfrm.pas' {frmVendorList},
      vendorlistsignrep in 'vendorlistsignrep.pas' {repVendorListSign: TQuickRep};{$R *.RES}
    var
      OApplication:TApplication;function ShowVendor(PApplication:TApplication;PForm:TForm):longint;stdcall;
    var
      oForm:TfrmVendor;
    begin
    //will to do...
      Application:=PApplication;
      oForm:=TfrmVendor.Create(application);
      oForm.ParentApplication:=PApplication;
      oForm.ParentForm:=PForm;
      result:=longint(oForm);
      oForm.Show;
    end;
    function ShowVendorMoneyManage(PApplication:TApplication;PForm:TForm):longint;stdcall;
    var
      oForm:TfrmPostMoney;
    begin
    //will to do...
      Application:=PApplication;
      oForm:=TfrmPostMoney.Create(application);
      oForm.ParentApplication:=PApplication;
      oForm.ParentForm:=PForm;
      result:=longint(oForm);
      oForm.Show;
    end;function ShowVendorLedgeQuery(PApplication:TApplication;PForm:TForm):longint;stdcall;
    var
      oForm:TfrmVLedgeQuery;
    begin
    //will to do...
      Application:=PApplication;
      oForm:=TfrmVLedgeQuery.Create(application);
      oForm.ParentApplication:=PApplication;
      oForm.ParentForm:=PForm;
      result:=longint(oForm);
      oForm.Show;
    end;function ShowVendorLedgeStat(PApplication:TApplication;PForm:TForm):longint;stdcall;
    var
      oForm:TfrmVLStat;
    begin
    //will to do...
      Application:=PApplication;
      oForm:=TfrmVLStat.Create(application);
      oForm.ParentApplication:=PApplication;
      oForm.ParentForm:=PForm;
      result:=longint(oForm);
      oForm.Show;
    end;
    function ShowVendorList(PApplication:TApplication;PForm:TForm):longint;stdcall;
    var
      oForm:TfrmVendorList;
    begin
    //will to do...
      Application:=PApplication;
      oForm:=TfrmVendorList.Create(application);
      oForm.ParentApplication:=PApplication;
      oForm.ParentForm:=PForm;
      result:=longint(oForm);
      oForm.Show;
    end;function ShowVendorAdjust(PApplication:TApplication;PForm:TForm):longint;stdcall;
    var
      oForm:TfrmVendorAdjust;
    begin
    //will to do...
      Application:=PApplication;
      oForm:=TfrmVendorAdjust.Create(application);
      oForm.ParentApplication:=PApplication;
      oForm.ParentForm:=PForm;
      result:=longint(oForm);
      oForm.Show;
    end;procedure DLLEntryPoint(dwReason:DWord);
    begin
      case dwReason of
        DLL_PROCESS_ATTACH:begin
                             OApplication:=Application;
    //                         HInstance:=Maininstance;
                           end;
        DLL_PROCESS_DETACH:Application:=OApplication;
        DLL_THREAD_ATTACH:;
        DLL_THREAD_DETACH:;
      end;
    end;exports
    ShowVendor,
    ShowVendorMoneyManage,
    ShowVendorLedgeQuery,
    ShowVendorLedgeStat,
    ShowVendorAdjust,
    ShowVendorList
    ;
    begin
    DllProc:=@DllEntryPoint;
    DllEntryPoint(DLL_PROCESS_ATTACH);end.
      

  12.   

    当然,通过每个字窗体自己控制的方法可能可以解决一些问题!
    可是,为什么DLL中的MDI就和普通的MDI用起来不一样呢?!:(为什么我现在在“揪”这个问题呢?!是因为我们现在的项目要用到这种“机制”,而且子窗体会有很多!如果不解决这些,重复的工作量太大!:(
      

  13.   

    procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
       Action:=caFree;
       Form1 := nil;  //你的程序里面最好加上,
    end;
    字体的原因是有些VCL控件你不能从外面传入,否则会有问题,而tab健我现在还没有找到答案
      

  14.   

    procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
       Action:=caFree;
       Form1 := nil; //这句加上
    end;字体的原因是有些VCL控件不能传入dll,TAb健的问题我也没有找到答案