我现在做的一个工程要求这样:主界面左侧是一个列表,这个列表内容可以中数据库读出来,鼠标单击列表内容,显示对应的设置窗体。但是领导要求:设置以后会逐步增加,所以这些设置的窗体要做活的,而且主程序以后就不修改了,要动态加载这些设置窗体。
我像这样解决:创建一些窗体放在dll中,然后主程序有一个配置文件,然后根据配置文件调用不同的dll中的窗体,一个dll放一个窗体。
但是问题出现了:我尝试建立ActivexForm工程,默认为ocx,我修改编译生成dll,我用regsver32进行注册,然后我再主界面加了一个TOLEContainer控件,想把这个ActivexForm放在这个上面,我的activeXForm类名是‘TMyAF’,然后在主程序中调用OleContainer1.CreateObject('TMyAF',false);运行到这一句出错,说是无效的类别字串,这是为什么?
还有activexForm和dll中的Form有什么区别?
大家看看我的这个做法是否可行?或者有可行的方案?
在线等

解决方案 »

  1.   

    对了,忘记一句:怎么样在主程序中不用添加引用,通过配置文件就可以加载的?
    我刚才的那个例子在主程序中还是引用了TLB文件,也就是说以后还是要修改主程序的,
      

  2.   

    active Form 没有做过,但是这个其实就是一个主程序,功能都放在 dll 中了,
    配置文件可以用数据库呀不要一个窗体放一个 dll ,dll 中可以放好多的窗体呀数据库中保存 dll 的导出函数就行了
      

  3.   

    这个只要
    1、建个表定义DLL文件名及DLL功能入口函数名
    2、主程序统一定义DLL的接口参数,以后新加的DLL按定好的格式调用就行了,
      

  4.   

    改DLL不一样是改程序吗,非要用 DLL 领导有毛病。
      

  5.   

    另外你的做法很奇怪:
    要么你就作成原始的DLL只输出函数的 在函数中调用你的窗口 一般不会出错
      

  6.   

    我对DLL不熟悉,没做过。用activeX form是因为注册方便,创建的时候仅需要类名称就可以了。
    但是在DLL中怎么定义界面和如何调用?我定义一个个Frame然后显示在我的主程序Panel上是否可以?
    还有DLL中的参数我是不是只能传递主程序的Handle?在dll中根据handle我怎么得到Panel?
      

  7.   

    我对DLL不熟悉,没做过。用activeX form是因为注册方便,创建的时候仅需要类名称就可以了。
    但是在DLL中怎么定义界面和如何调用?我定义一个个Frame然后显示在我的主程序Panel上是否可以?
    还有DLL中的参数我是不是只能传递主程序的Handle?在dll中根据handle我怎么得到Panel?
      

  8.   

    我在DLL中加入的Form(或者Frame都不行),然后返回创建的窗体。主程序接受返回的窗体,然后显示在Panel上,但是问题是接受的窗体用ShowModal,可以正常显示。但是显示在Panel上就不显示,并且退出主程序是报错。DLL和主程序代码如下,请大虾们看看是怎么一回事情:
    DLL代码:
    library Project1;uses
      ShareMem,
      SysUtils,
      Classes,
      ExtCtrls,
      Forms,
      System,
      Dialogs,
      Unit1 in 'Unit1.pas' {Form1};{$R *.res}
    var
    { 用于初始化:保存DLL本身的Application,然后设置DLL的Application指向Host的Application }
      DllApp:TApplication;  procedure InitDll(MainApp:TAppLication);stdcall;
      begin
        DllApp:=Application;
        Application:=MainApp;
      end;  procedure FreeDll;stdcall;
      begin
        Application:=DllApp;
      end;  function GetForm(AParentControl:TComponent):TForm1;stdcall;
      begin
        result:=TForm1.Create(AParentControl);
      end;exports
      GetForm,InitDll,FreeDll;begin
    end.主程序代码:
    unit Unit1;interfaceuses
      ShareMem,Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, ExtCtrls, StdCtrls;type
      TfrmMain = class(TForm)
        Button1: TButton;
        Panel1: TPanel;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      frmMain: TfrmMain;implementation
    {$R *.dfm}
    function GetForm(AparentControl:TComponent):TForm;stdcall; External 'project1.dll ';
    procedure InitDll(MainApp:TApplication);stdcall; External 'project1.dll ';
    procedure FreeDll;stdcall; External 'project1.dll ';procedure TfrmMain.Button1Click(Sender: TObject);
    begin
      with GetForm(self) do
      begin
        //ShowModal;
        Parent:=self.Panel1;
        Visible:=true;
      end;
    end;procedure TfrmMain.FormCreate(Sender: TObject);
    begin
      initdll(Application);
    end;procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      freeDll;
    end;procedure TfrmMain.Button2Click(Sender: TObject);
    begin
     //这段是主程序里面建立的和DLL中一样的Form,就可以正常显示 
     if asd<>nil then
      begin
        asd.Free;
        asd:=nil;
      end;
      asd:=TForm3.Create(self);
      asd.Parent:=self.Panel1;
      asd.Visible:=true;
    end;end.
      

  9.   

    dll中的application 和主程序的applicaiton是不同的,雖然可以欺騙式的將主程序的給dll,但實際windows是有作保護措施的,所以dll中的form要訪問主程序的panel(dllform.parent:=mainform.panel),會無法訪問;
    建議使用API: SetParent(Dllform.Handle,mainform.Panel.Handle);
    另外,建議你把整個程序的架構要分清,建議寫一個主呼叫的dll,在這個dll裡面,根據參數來判斷要呼叫哪一個dll,主程序只需要調用這個dll所exports出來的函數,傳進不同的參數,比如可以通過tag來區分不同的form。這樣就不用修改主程序了,也比較安全。
    建議application的切換,寫在dll裡面,可以這樣:
    procedure DLLEntryPoint(dwReason: DWord);
    begin
      case dwReason of
        DLL_PROCESS_ATTACH:;//在這裡記錄原來的application
        DLL_PROCESS_DETACH:;//在這裡還原原來的application 
        DLL_THREAD_ATTACH: ;
        DLL_THREAD_DETACH: ; 
      end;
    end;begin
      DllProc :=@DLLEntryPoint;
      DLLEntryPoint(DLL_PROCESS_ATTACH);
    end.
      

  10.   

    请问楼上两个问题:
    1,DLLEntryPoint的这个方法怎么使用?那个主程序的Application怎么传进来的?
    2,如果采用你说的有一DLL专门负责提供调用那个DLL文件和哪个方法,是不是就要使用动态加载DLL的方法了?那我加载几个DLL中的Form,我什么时候释放?怎么释放?我加载一个DLL,在显示一个窗体到Panel上,然后就释放DLL?如果我再调用这个DLL中的另一个窗体,我是不是还要加载一次?
    还有经过这两天的尝试,发现两个主要的问题:
    1,DLL中的Form不能用传进来的Panel来Create,要使用传进来的Application来创建;
    2,设置Parent确实不能用dllform.parent:=mainform.panel,只能使用API的SetParent方法
    3,如果要显示在Panel上的是无标题框的form,不能将form放入类型设置为None,也不要将Align设置为alClient,做法是设置windowstate为Max,然后去掉标题栏窗口,如我使用如下:
      windows.SetParent(test.Handle,self.Panel1.Handle);
      windows.SetWindowLong(test.Handle,GWL_STYLE,GetWindowLong(test.Handle,GWL_STYLE) and not(WS_CAPTION or WS_THICKFRAME));
      test.WindowState :=  wsMaximized;
      

  11.   

    回復lz兩個問題
    1,直接寫在工程文件裡面.主程序的application可以在getform時傳入,在這裡進行切換;
    2,是的,用動態加載的方法。關於釋放的問題,你可以在主呼叫dll裡面再寫一個函數來管理這些加載進來的dll,export出去,在主程序裡,你可以選擇釋放的時機(個人認為,顯示完就釋放,方便管理,避免內存洩漏)
      

  12.   

    非常感谢楼上的回答,分数大部分都是你的了呵呵。不过又有两个问题需要解决一下:
    1,动态调用DLL显示之后,使用的是SetParent方法,但是Panel的ControlCount属性为什么还是0呢?
    2,如果DLL中的窗体在CloseQuery方法里有阻止窗体关闭的代码,主程序怎么才能知道我调用Close之后是关闭还是阻止关闭了呢?因为我要显示下一个窗体了?
      

  13.   

    回復如下:
    1、由於是采用API強制嵌入,而不是設置parent屬性,因此panel並沒有收到notify,controlcount也就為0了;
    2、在主程序裡面,可以設定一個變量IsClose來標識dllform是否關閉,自定一個消息(比如WM_IsClose),寫一個相應處理過程;
    在dll裡也定義這樣的消息類型,你可以在closequery裡面發送消息給主程序,如SendMessage(mainform.handle,WM_IsClose,0,1)或者(mainform.handle,WM_IsClose,0,0)通過判斷lparam來判斷是否關閉
      

  14.   

    補充一點:你可以在消息處理過程裡面去開下一個dllform;因為我猜你可以能是想在關閉窗體後,自動開啟列表中的下一個dllform
      

  15.   

    使用消息可能是麻烦一点,可能需要注册新消息。我现在的解决方法是主程序传Application给DLLForm的Application,DLLForm在Close的时候,设置Application的Tag,主程序通过访问这个Tag来判断,呵呵。结贴了