高分求助, 这段在代码放在exe中没有问题, 放在DLL中就有问题, IS的问题吗?

cncharles 2006-04-28 04:11:49
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
...全文
354 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
cncharles 2006-06-24
  • 打赏
  • 举报
回复
问题没有搞定, 但是还是把贴结了
wizardqi 2006-06-13
  • 打赏
  • 举报
回复
没道理呀,怎么不会为真呢,以下是我模拟的代码。
//动态库部分
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;

interface

uses
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.


huitouren519 2006-06-12
  • 打赏
  • 举报
回复
不太懂 学习
数字蛋糕 2006-06-09
  • 打赏
  • 举报
回复
不好意思,很长时间没有上来,没看到你的留言。

所谓的用接口并不是说实现一个接口就能够解决所有的问题。因为你的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的方法而不是别的什么模块中的同名类。

说了很多废话,但愿能够对你有点帮助:)
cncharles 2006-05-11
  • 打赏
  • 举报
回复
自已顶
cncharles 2006-05-11
  • 打赏
  • 举报
回复
up
cncharles 2006-05-10
  • 打赏
  • 举报
回复
谢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;

真搞不懂是什么原因.
budded 2006-05-08
  • 打赏
  • 举报
回复
共享RTL, VCL似乎解决不了这个问题……
我的解决方案:不用is来判断,而是用一个While来判断一个类及其父类的Classname是否相同,
这个方法似乎很笨,
数字蛋糕 2006-05-08
  • 打赏
  • 举报
回复
我前面已经说过了,在不同模块之间传递对象最可靠的是使用借口。强制转换类型是不行的。因为通过类名进行识别并不可靠。
ffwin 2006-05-08
  • 打赏
  • 举报
回复
设断点调试一下不就行了,如果不会调试dll请查书
cncharles 2006-05-08
  • 打赏
  • 举报
回复
自已顶
cncharles 2006-05-07
  • 打赏
  • 举报
回复
问题还没有解决, 如果不用Build With Runtime package
有没有其它办法让两个本来相同的类强制类转换不出错呢 ?

用ShareMem, FastMM都没有解决, 郁闷.
数字蛋糕 2006-05-02
  • 打赏
  • 举报
回复
“DataSet Action就都失效了”是什么意思?
cncharles 2006-04-29
  • 打赏
  • 举报
回复
cybercake(数字蛋糕

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

这种方法可行但是要用CoInitialize才行否则会报错,
用了CoInitialize窗体上的DataSet Action就都失效了这个问题又怎么解决?
数字蛋糕 2006-04-28
  • 打赏
  • 举报
回复
记得Exe和Dll都要这么干
数字蛋糕 2006-04-28
  • 打赏
  • 举报
回复
选择菜单project->option->Packages->把Runtime-Packages的那个CheckBox勾上
cncharles 2006-04-28
  • 打赏
  • 举报
回复
cybercake(数字蛋糕)

最简单解决的方法是使用动态编译,把包含相关控件的包分离出去。

给个DEMO
数字蛋糕 2006-04-28
  • 打赏
  • 举报
回复
再DLL中用is判断总是容易出问题,因为你的exe和dll都包含了一个vcl类库的副本,所以在内存中同名的Class并不表示Class的内容是相同的。(想象一下你两次编译用的是不同的vcl版本)

最简单解决的方法是使用动态编译,把包含相关控件的包分离出去。

最可靠的方法是使用带GUID的接口把相关的类重新封装一次。
wsalvin 2006-04-28
  • 打赏
  • 举报
回复
问题可能出在FStrings.Objects[Index] 之中
aiirii 2006-04-28
  • 打赏
  • 举报
回复
if DataSet.InheritsFrom(TClientDataset) then
测试如何??
加载更多回复(8)

5,388

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧