function DataChanged(Index:Integer):Boolean;
  var
    DataSet:TObject;
  begin
    DataSet:=FStrings.Objects[Index];
    //ShowMessage(DataSet.ClassName); //能正确显示 TClientDataSet
    if DataSet is TClientDataSet then begin
      Cds:=TClientDataSet(DataSet);
      if (Cds.ChangeCount>0) or (Cds.Modified) then begin
        Result:=True;
        Exit;
      end;
    end;
    Result:=False;
 end;为什么这个函数放到DLL中 if DataSet is TClientDataSet then
不会真, 但是在EXE中就可以呢?开发平台是Delphi2006, XP+SP2

解决方案 »

  1.   

    为什么这个函数放到DLL中 if DataSet is TClientDataSet then
    不会真, 但是在EXE中就可以呢?DataSet  是不是在主exe 传过去的?? 如果是,就好解释
      

  2.   

    DLL就是这么麻烦,简单的办法是用放在一个窗口上
      

  3.   

    不是的, 这保存在DLL中FStrings.Objects中的
    完整的函数如下
    function TActiveDataSet.Changed(Index:Integer):Boolean;
    var
      Cds:TClientDataSet;
      I:Integer;  function DataChanged(Index:Integer):Boolean;
      var
        DataSet:TObject;
      begin
        DataSet:=FStrings.Objects[Index];
        //ShowMessage(Dataset.ClassName)
        if DataSet is TClientDataSet then begin
          Cds:=TClientDataSet(DataSet);
          if (Cds.ChangeCount>0) or (Cds.Modified) then begin
            Result:=True;
            Exit;
          end;
        end;
        Result:=False;
     end;begin
      Result:=False;
      if Index>=FStrings.Count-1 then begin
        for I:=0 to FStrings.Count-1 do
          if DataChanged(I) then begin
            Result:=True;
            Exit;
          end else begin
            Result:=False;
          end;
      end else
        Result:=DataChanged(Index);
    end;//类结构如下
    TActiveDataSet=Class(TComponent, IDBase, IActiveDataSet)
      private
        FIDBase:IDBase;
        FStrings:TStringList;
      protected
        property DBase:IDBase read FIDBase implements IDBase;
      public
        //IActiveDataSet interface
        function Changed(Index:Integer):Boolean;
        function SetActiveDataSet(DataSet:TDataSet):Integer;
        function GetActiveDataSet:TDataSet;
        function GetActiveDataSets:TStrings;
        procedure SaveData;overload;
        procedure SaveData(DataSets:TStrings);overload;    constructor Create(AOwner:TComponent);override;
        destructor Destroy;override;
      end;
      

  4.   


     showMessage(DataSet.ClassName);
     if DataSet is TClientDataSet then begin
      

  5.   

    if DataSet.InheritsFrom(TClientDataset) then
      测试如何??
      

  6.   

    问题可能出在FStrings.Objects[Index] 之中
      

  7.   

    再DLL中用is判断总是容易出问题,因为你的exe和dll都包含了一个vcl类库的副本,所以在内存中同名的Class并不表示Class的内容是相同的。(想象一下你两次编译用的是不同的vcl版本)最简单解决的方法是使用动态编译,把包含相关控件的包分离出去。最可靠的方法是使用带GUID的接口把相关的类重新封装一次。
      

  8.   

    cybercake(数字蛋糕)最简单解决的方法是使用动态编译,把包含相关控件的包分离出去。给个DEMO
      

  9.   

    选择菜单project->option->Packages->把Runtime-Packages的那个CheckBox勾上
      

  10.   

    cybercake(数字蛋糕选择菜单project->option->Packages->把Runtime-Packages的那个CheckBox勾上这种方法可行但是要用CoInitialize才行否则会报错, 
    用了CoInitialize窗体上的DataSet Action就都失效了这个问题又怎么解决?
      

  11.   

    “DataSet Action就都失效了”是什么意思?
      

  12.   

    问题还没有解决, 如果不用Build With Runtime package
    有没有其它办法让两个本来相同的类强制类转换不出错呢 ?用ShareMem, FastMM都没有解决, 郁闷.
      

  13.   

    设断点调试一下不就行了,如果不会调试dll请查书
      

  14.   

    共享RTL, VCL似乎解决不了这个问题……
    我的解决方案:不用is来判断,而是用一个While来判断一个类及其父类的Classname是否相同,
    这个方法似乎很笨,
      

  15.   

    谢cybercake, 我本来用的就是接口. 
    TActionMenu=class(TComponent, IActionMenu)
      private
        FActionBarItem:TActionBarItem;
      public
        //IActionMenu
        procedure AddMenu(const Dimensions:array of Byte; Action:TAction);
        procedure DeleteMenu(const Dimensions:array of Byte);
        function GetActionBarItem:TActionBarItem;
        function GetParentActionItem(const Dimensions:array of Byte):TActionClientItem;
        procedure InsertMenu(const Dimensions:array of Byte; Action:TAction);
        procedure SetActionBarItem(AActionBarItem:TActionBarItem);
        property ActionBarItem:TActionBarItem read GetActionBarItem write SetActionBarItem;
      public
        constructor Create(AOwner:TComponent);override;
        destructor Destroy;override;
      end;//GetParentActionItem不会出错, 可以正确的回转结果.
    function TActionMenu.GetParentActionItem(
      const Dimensions: array of Byte): TActionClientItem;
    var
      I:integer;
      Msg:string;
      ActionItem:TActionClientItem;
      ActionClients:TActionClients;
    begin
      Result:=nil;
      ActionItem:=nil;
      Msg:='对不起你的第%d维值%d超标.';
      ActionClients:=FActionBarItem.Items;
      for I := 0 to High(Dimensions) - 1 do begin
        if Dimensions[I]>ActionClients.Count then begin
          Msg:=Format(Msg, [I, Dimensions[I]]);
          ShowMessage(Msg);
          Exit;
        end else begin
          ActionItem:=TActionClientItem(ActionClients.Items[Dimensions[I]]);
          ActionClients:=ActionItem.ActionClients;
        end;
      end;
      Result:=ActionItem;
    end;
    procedure TActionMenu.AddMenu(const Dimensions: array of Byte; Action: TAction);
    var
      ActionItem:TActionClientItem;
    begin
      ActionItem:=GetParentActionItem(Dimensions);
      if ActionItem=nil then
        Exit;
      ActionItem:=ActionItem.Items.Add; //这里就出错. 类型不一致, 也就是说只要用到了
      //ActionClientItem.Items属性都会出错, 但是奇怪能实现我的功能,但总提示错误不好.
      ActionItem.Action:=Action;
    end;真搞不懂是什么原因.
      

  16.   

    不好意思,很长时间没有上来,没看到你的留言。所谓的用接口并不是说实现一个接口就能够解决所有的问题。因为你的ActionItem.Items.Add返回的仍然是一个类实例(我猜的),所以所有的操作都是通过类,而不是通过接口来进行的。我再简单的解释一下用类名称(也就是is)为什么不可靠。比如说你再dll中写了一个类TMyClass,这个类有一个方法名叫Method1。同时道出一个函数GetMyObject:TObject,返回一个TMyClass实例。写好后编译成一个dll。
    然后在主程序中写一个类,名字也叫做TMyClass。这个类有一个方法名叫Method2。写好后编译成exe。这下问题来了,你在主程序中使用obj = GetMyObject。这时obj的类名称是TMyClass。但是这个TMyClass并不是你在exe中定义的那个TMyClass。如果你在exe中这样写TMyClass(obj).Method2完全可以通过编译。但是被执行的代码完全有可能是Method1。这个问题的根源在于exe和dll是分开编译的。编译工具不可能有机会检查外部模块代码的类型是否匹配,因为这些代码根本就不在当前工程中。退一步来说,如果exe和dll中的TMyClass类声明完全相同,但这个相同也只是停留在语法(Syntax)的层面上,在语义(Semantic)上他们完全有可能迥异,尤其是它们的数据段不同的时候,问题将会更加突出。对于接口,因为它只有声明没有实现,所以首先它保证了语法上的一致性(当然如果你更改了接口而忘了编译所有涉及的模块,那一样会出问题)。对于采用了GUID的接口,GUID使得所有的类型都能够被唯一的识别,基本上杜绝了重名的现象(故意重用GUID的不算)。其次,接口唾弃了数据段的存在。这样一来所有对数据的访问都被限制在实现类(Implementation)的范围内。故而不会发生Class1的方法要访问Class2的数据。然后重新放在exe和dll的环境中。如果一个方法只返回一个接口(例如GetMyObject:IMyinterface),那么保存在接口的类型信息中的就只会是一系列方法的地址,而这些地址都会指向各自的实现类的方法的地址。所以你在exe中调用IMyInterface.Method1的时候就能够确定调用的是dll中的TMyClass的Method1的方法而不是别的什么模块中的同名类。说了很多废话,但愿能够对你有点帮助:)
      

  17.   

    没道理呀,怎么不会为真呢,以下是我模拟的代码。
    //动态库部分
    library Test;uses
      Windows,
      SysUtils,
      Classes,
      DBClient;{$R *.res}
    var
      FStrings:TStringList;
      procedure Init;stdcall;
      begin
        FStrings:=TStringList.Create;
        FStrings.AddObject('CDS',TClientDataSet.Create(nil));
      end;
      procedure Final;stdcall;
      begin
        if Assigned(FStrings) then
        begin
          FStrings.Clear;
          FStrings.Free;
        end;
      end;
      procedure TestIS;stdcall;
      var
        DataSet:TObject;
        ClassName:String;
      begin
        DataSet:=FStrings.Objects[0];
        ClassName:=DataSet.ClassName;
        if DataSet is TClientDataSet then
          MessageBox(0,PChar(ClassName),'测试',0);
      end;exports
      Init,Final,TestIS;
    begin
    end.
    //以下是测试程序主窗体代码
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure FormCreate(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;implementation
    procedure Init;stdcall; external 'Test.dll';
    procedure Final;stdcall; external 'Test.dll';
    procedure TestIS;stdcall; external 'Test.dll';
    {$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);
    begin
      Init;
    end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      Final;
    end;procedure TForm1.Button1Click(Sender: TObject);
    begin
      TestIS;
    end;end.