现在我想从外部DFM中动态创建Form,其他都搞定了,就是遇到一个很棘手问题:
假设这个Form的类是TForm1,但是由于我没有办法RegisterClass(TForm1)(因为我程序中没有TForm1的类,只是这个DFM中有),会导致一个错误说我TForm1没有注册,假设将TForm1变为TForm是没有问题的,如果我有办法欺骗Delphi Runtime创建一个ClassName='TForm1'的类然后注册就可以,但是我试了许多方法,诸如使用vmtClassName试图改变其名称但失败了(好像是内存不能写但Evaluate居然可以~),也试图自己产生一个TClass然后注册但是很复杂,请问谁有这方面的经验?不胜感激!

解决方案 »

  1.   

    JVCL中有一个解释器,能动态运行dfm窗体,可以参考。
      

  2.   

    不是不行,我以前玩过,
    关键是没有什么用处,不知道你的具体需求是怎样的比如说,是怎样的Form,Form是自定义的,还是默认从TForm上继承的?Form上是否只有控件?没有事件?如果单单是想载入一些Form的属性以及上面的控件,问题应该不大
    不过没什么用途
      

  3.   

    这是我们的脚本构件的最后几个重要问题之一。现在已经功能比较强大了。迟些视公司的讨论如何看能否公开一个全功能的版本。脚本语言采用Object Pascal (Delphi)语言子集,基本语法与Object Pascal类似,但存在如下限制:
    1. 不支持Class、Interface定义,但可以通过嵌入编译方式将类、接口加入脚本
    2. 暂不支持forward函数
    3. 暂不区分unit/program分别,interface/implementation的定义都是公开的,暂不支持单元的interface函数定义预定义(忽略)
    4. 暂不支持sets类型定义,但支持已内置集合的字符串方式使用
    5. 暂对指针操作不完备
    6. 不支持with语法主要实现如下功能:
    1. 基本语法:类型声明、变量声明、赋值、循环(for/repeat/while/)、分支(if/case)、函数/过程
    2. 支持SEH:try..except..end和try..finally..end
    3. 支持二进制回调函数(Callback)定义和调用
    4. 支持函数类型声明和传递
    5. 支持DLL函数定义和调用
    6. 支持OLE/ActiveX调用
    7. 提供内置调试功能
    8. 支持嵌入编译方式将类、接口加入到脚本功能,支持这些类、接口的定义和使用
    9. 支持对象事件的脚本绑定
    10. 已导入Controls, StdCtrls, ExtCtrls, Forms, Grids, Mask, DB, ADODB 等多个界面单元
    11. 支持ZipCompress,多种加密数据方法
    12. COM接口支持GetUnkVar/SetUnkVar,可以在运行阶段动态取得、修改用户变量
    13. COM接口支持GetUnkProp/SetUnkProp,可以在运行阶段动态取得、设置界面、内部属性
    14. 支持SetLocalVar/GetLocalVar,可以在运行阶段通过名字字符串取得、设置变量
    15. 支持中文标识符(变量、类型、函数)
    16. 支持装载外部DFM绑定脚本
      

  4.   

    我现在只要求Form是TForm的继承品,能够完成用Delphi定义Form界面然后绑定到脚本中。以下是一些脚本例子:
    program LoadForm;{
    版本历史:
    日期        修改人     内容
    2004-08-27  神秘人     初始版本
    }uses
      SysUtils, Dialogs, Forms;var
      cForm: TForm;procedure Button1Click(Sender: TObject);
    begin
      ShowMsg('Button1Click');
    end;procedure Edit1Change(Sender: TObject);
    begin
      ShowMsg('Edit1Change');
    end;begin // 主程序入口
      cForm := LoadComponentFromFile('D:\Edward\SOURCE\AW\TestForm.dfm');
      if cForm<>nil then
        cForm.ShowModal;
    end.
    /////////////////////////program ADOTest;{
    版本历史:
    日期        修改人     原因
    2004/06/07  神秘人     初始版本
    }uses
      SysUtils, Dialogs, ExtDialogs, Classes, DB, ADODB;  procedure MyBeforeOpen(DataSet: TDataSet);
      begin
        ShowMsg(Format('Before Open, Class=%s, State=%d', [DataSet.ClassName, DataSet.State]));
      end;  procedure MyAfterOpen(DataSet: TDataSet);
      begin
        ShowMsg('After Open, State='+IntToStr(DataSet.State));
      end;var
      cQry: TADOQuery;
      cDS: TClientDataSet;
    begin // 主程序入口
      cQry := TADOQuery.Create(nil);
      cQry.ConnectionString := 'Provider=SQLOLEDB.1;Data Source=127.0.0.1;Password=tmc1234;Persist Security Info=True;User ID=tmc;Initial Catalog=tmc';
      AttachEvent(cQry, 'BeforeOpen', MyBeforeOpen);
      AttachEvent(cQry, 'AfterOpen', MyAfterOpen);
      cQry.SQL.Clear;
      cQry.SQL.Add ('select * from idtbl');
      cQry.Active := TRUE;
      cDS := TClientDataSet.Create(nil);
      SelectFromDataSet ('idtbl rows',cQry,'',cDS);
    end.////////////////////////////////function 校验日期型数据项为空(yEltNames: OpenArray): Boolean;
    var
      I: Integer;
      dt: TDateTime;
    begin
      for I:=Low(yEltNames) to High(yEltNames) do
      begin
        dt := GetUnkVar(yEltNames[I]);
        if dt = 0 then
        begin
          ShowMessage('请录入' + yEltNames[I]);
          Result := TRUE;
          Exit;
        end;
      end;
      Result := FALSE;
    end;program Backup;{
    版本历史:
    日期        修改人     原因
    2004-06-29  EdGuo     初始版本
    }uses
      FileUtils, Dialogs, DirUtils, SysUtils, OS, Compress;var
      bAborted: Boolean;  function MyProgress (sFile: string; nCur, nTotal: Integer; sToPath: string): Integer;
      var
        n: Integer;
      begin
        if nCur>=nTotal then n := 0 else
          n := nCur div 16384;
        if Odd(n) then
        begin
          Result := 0;
        end else
        begin
          if UpdateProgressTitle(sFile+#13+'复制到:'+sToPath, nTotal, nCur) then
            Result := 0 else
          begin
            Result := 1;
            bAborted := TRUE;
          end;
        end;
      end;  function Pack (sPath: string): Boolean;
      var
        sParam, sPW, sFile: string;
        nExitCode: Integer;
        b: Boolean;
        cZip: TZipper;
      begin
        Result := FALSE;
        sPW := '';
        sFile := 'c:\backup.zip';
        if InputNewPassword(sPW, 4, '输入备份密码') then
        begin
    lblInputFile:
          if InputQuery('输入', '目标文件名:', sFile) then
          begin
            if sFile<>'' then
            begin
              if FileExists(sFile) then
              begin
                b := FALSE;
                case AskUser('文件已存在:'+sFile+#13+'是:覆盖, 否:重新输入, 取消:取消操作') of
                1: DeleteFile(sFile, FALSE);
                2: b := TRUE;  // should not use goto in case statement
                3: Exit;
                end;
                if b then goto lblInputFile;
              end;          cZip := TZipper.Create(sFile);
              cZip.BaseDirectory := sPath;
              cZip.Password := sPW;
              cZip.ShowProgress := TRUE;
              cZip.Replace := TRUE;
              cZip.Recurse := TRUE;
              cZip.AddFiles ('*.*');
              try
                cZip.Save;
              except
              end;
              cZip.Free;
              Result := TRUE;          {
              sPath := AddDirSlash(sPath)+'*.*';
              sParam := Format('rar a -r -p%s %s %s',[sPW, sFile, sPath]);
              nExitCode := -1;
              if Run(sParam, $22, 0, nExitCode) then
              begin
                if nExitCode=0 then
                  Result := TRUE;
              end;
              }
            end;
          end;
        end;
      end;var
      sSource, sTarget: string;begin // 主程序入口
      if ParamCount<2 then
      begin
        ShowMsgTimeOut('错误','没有定义源和目标路径',5);
        Halt(1);
      end else
      begin
        sSource := ParamStr(1);
        sTarget := ParamStr(2);
        bAborted := FALSE;
        BeginProgress('正在复制...', 2, '复制进度', 0);
        FileCopyList(sSource, sTarget,
    //      '*.pas',
          '*.pas;*.dfm;*.dpr;*.inc;*.cfg;*.dof;*.bpg;*.dpk;*.rc;*.res;*.r32;*.bmp;*.dcr;*.tlb;*.jpg;*.txt;*.obj',
          TRUE, 7, MyProgress);
        EndProgress;
        if not bAborted then
        begin
          if Pack (sTarget) then
            ShowmsgTimeOut('信息','完成备份:'+sSource+#13+'到:'+sTarget,5);
          DelDir (sTarget, true, true);
        end;
      end;
    end./////////////////////////////program all1;
    uses
      SysUtils,Classes,Dialogs,Math,DateTimeUtils,StrUtils,
      COMObj,Variants,EncdDecd,DB,ADODB,XML,IniFiles,
      ExtDialogs,Graphics,Controls,StdCtrls,Forms,Mask,Masks,
      ExtCtrls,Grids,DirUtils,FileUtils,ConwinAW,AWWizard,Encrypt,
      Compress,DebugLog,OS,Hardware,Chinese,CRC,Validate,
      CWSPlugin;begin
      Sleep(20);
    end.////////////////////////////program NCall;{
    版本历史:
    日期        修改人     内容
    2004-07-08  神秘人     初始版本
    }uses
      SysUtils, Dialogs;
      procedure test1(var n1: Integer);
      begin
        if n1>100 then Exit;
        n1 := n1+1;
        test2(n1);
      end;  procedure test2(var n2: Integer);
      begin
        if n2>100 then Exit;
        n2 := n2+2;
        test1(n2);
      end;
    var
      n3: Integer;
    begin // 主程序入口
      test1(n3);
    end.////////////////////////////////////
    program MyForm1;{
    版本历史:
    日期        修改人     内容
    2004-08-09  神秘人     初始版本
    }uses
      SysUtils, Dialogs, Graphics, Controls, StdCtrls, Forms, Mask;var
      cForm: TForm;
      cBtn: TButton;
      cEdit: TMaskEdit;  procedure OnOKClick (cSender: TObject);
      var
        cMyBtn: TButton;
        r: TRect;
      begin
        cMyBtn := cSender;
        ShowMsg(Format('Call from %s, EditText=%s',[cMyBtn.Caption, cEdit.Text]));
        if AskUser1('是否OK?')=1 then cForm.ModalResult := mrOK else
        begin
          r := cForm.BoundsRect;
          r.Left := r.Left+30;
          r.Right := r.Right+30;
          r.Top := r.Top+25;
          r.Bottom := r.Bottom+25;
          cForm.BoundsRect := r;
        end;
      end;begin // 主程序入口
      cForm := TForm.Create (nil);
      cForm.SetBounds (30, 100, 300, 200);  cForm.Caption := 'test form';
      cEdit := TMaskEdit.Create (cForm);
      cEdit.EditMask := '9999-99-99';
      cEdit.Top := 10;
      cEdit.Left := 10;
      cEdit.Width := 100;
      cForm.InsertControl(cEdit);
      cBtn := TButton.Create (cForm);
      cBtn.SetBounds (30, 50, 80, 25);
      cBtn.Caption := '&Hello';
      //cBtn.ModalResult := mrOK;
      AttachEvent(cBtn, 'OnClick', OnOKClick);
      cForm.InsertControl(cBtn);  if cForm.ShowModal=mrOK then
        ShowMsg('World!');
      cForm.Free;
    end.
    /////////////////////program CaseTest;  // todo: 修改名称{
    版本历史:
    日期        修改人     原因
    2004-06-26  神秘人     初始版本
    }uses
      SysUtils, Dialogs;var
      s: string;
      n1,n2: Integer;
    begin // 主程序入口
      case Random(5) of
      0: s := 'what';
      1: s := 'is';
      2: s := 'What';
      else s := 'it';
      end;  n1 := 0;
      case s of  // Lowercase(s)
      'what':
        begin
          case n1+Random(8) of
          n1..n1+2:
            begin
              ShowMsg('n1..n1+2');
            end;
          n1+3,n1+4:
            begin
              ShowMsg('n1+3, n1+4');
            end;
          else
            begin
              ShowMsg('else 2');
            end;
          end;
          ShowMsg('case WHAT');
        end;
      'is':
        begin
          ShowMsg('case IS');
        end;
      else
        ShowMsg('else: '+s);
      end;
    end.
      

  5.   

    靠!
    我真TMD silly!之前已经发觉TReader如果传入非空的Root就不会调用FindClass,但是因为使用TForm.Create而导致Resource错误所以没用,后来发觉只要使用TForm.CreateNew造假就行了,现在问题解决了,虽然还不能面对所有情况,但是只要能应付TForm就够了,因为最大的目的是用来做界面。真愚昧!Thanks anyway.
      

  6.   

    迟几天结贴,dejoy=20, alphax=10.其余剩余的只要有人跟贴就各人平分。另外可在此留言索要一个EXE运行版本的Scripter,可以在命令行下运行脚本,对于编程可能没有什么用,但用于维护和玩玩还是可以,当然,我不对这种release负任何责任;COM版本暂时不能公开(Sorry).脚本的功能和例子见上。
      

  7.   

    RemObject PascalScript不是更强大+免费开源的Delphi脚本引擎吗?
    不知道你有何优势呢?
    商业上没优势就难有市场了
      

  8.   

    我们的Scripter应该比IFPS要好,而且支持中文标识符,内置有调试器,支持严格的数据结构,不象大多数的Scripter只用Variant倒来倒去,不过我们开发Scripter不是为了卖Scripter,而是对其他项目产品提供支持,以便更灵活、更高效地去开发其他功能,对程序员的要求也相对较低,甚至可以做到给高级客户修改。当然还有许多不足,但已经实际使用到项目产品中。
      

  9.   

    关于这样对象串行化的技术,相当有实用价值的。
    我以前也花了一点功夫在上面,VCL FORM既然可以串行化DFM格式,自然也可以串行化
    XML格式;可以为分布式系统提供多种可能性;也可以成一种文档编辑查看工具。。
    呵呵。吹远了。。
      

  10.   

    象楼主这种应用,其实用package完全可以胜任,为什么还要用脚本,有什么实实在在的好处?给我说说吧
      

  11.   

    这仅仅是增强脚本界面功能的一个途径.package在我们上一代项目(D4)产品中被大量使用, 其中业务DLL应已超过1000个, 配合自动更新功能一定程度说是达到了目的, 不过其问题是显而易见的.使用Script我认为至少有如下好处:
    ◎可控性:你的程序员被限制在你的开发环境中,避免了许多不必要的错误,其错误状态也更易于捕捉
    ◎可维护性:脚本可以存储在任何地方,在我们是我们自己开发的网络文件系统(NFS)并通过自己的应用服务器存取,一旦更新就能全部更新。可以对不同的客户编写不同的同类型脚本,其发布和修改成本是很低的,甚至对高级客户而言可以由他们自己修改/创建 (我们的脚本支持中文identifier)
    ◎对程序员更低的要求: 只需要稍微懂Delphi培训2周就可以开始工作,可以把大量的工作交给其他人而他们不必要对技术过分的认知。
    ◎如果你有版本控制,我认为脚本的版本比二进制版本要稍微直观点。
    ……
      

  12.   

    谢谢,没有实际应用经验,所以对“对程序员更低的要求”这一点的理解比较模糊,我看你们开发的脚本还是挺复杂的,特性也蛮多的,好像没有降低什么复杂性啊,,,从另一个角度说,如果脚本的功能不强,对程序员的要求自然不高,但是却做不了什么你这个问题,我还有一个疑问就是,你是直接载入dfm文件,还是res文件?
      

  13.   

    你可以这样想象, 如果一个新手面对一个新的开发环境, 他/她通常会有什么障碍?对开发环境不熟悉是肯定的, 但通常更多的是不知如何入手,缺乏文档资料(这我我们的流程管理能力有关),对一些系统特殊的地方不理解,觉得很茫然;其实多数的二次开发系统都有一个培训和控制的困难,严格来说Compiler也算一个二次开发系统,但是她往往给予程序员的自由度过大(想象一下你经常去下载各种Component来测试),这当然会分心;另外一方面很多平台都提供了羞涩的API和参数结构传递,想象一下对于一个英语不大行的使用QueryAccountInfoFromUserInputWithFilters()和"根据用户录入过滤查询台帐()"的区别以及解释传入参数结构中OnValidate事件是如何使用的……这些都是现实的东西,对于一个需要许多人参与的大量面对客户业务逻辑的项目,如何尽快培养出能写业务的人是很重要的,因为工作已成为工作,不再是程序艺术。当然,我们还有很多工具/制度尽量保证其简易性和流程的执行,例如 设计业务/界面制作/业务脚本开发/测试/安装/版本控制 都分开,需要正规签领手续才能交接等。当然,我们也只是在尝试摸索出合乎现阶段经济效益的软件项目/产品生产流程。直接装入DFM。