我正在尝试编写一个组件,它正在从obj文件中加载3D对象。我正在使用ToolsAPI库作为GetActiveProject.FileName。我将designide.dcp添加到bpl中的Requiers部件中。我注册了我的对象,在设计中,当我将这个对象的一个实例放在一个TViewPort3D上时,在一切正常之前,我可以从obj文件中看到这个对象被加载到场景中,但是当我试图编译这个项目时,我会得到一个错误,上面写着ToolsAPI.dcu没有找到。
加载obj文件的过程是( Model变量的类型为TModel3D):
procedure TMyObject.LoadModel(fileName: string);
begin
if(csDesigning in ComponentState)then
Model.LoadFromFile(IncludeTrailingPathDelimiter(ExtractFilePath(GetActiveProject.FileName))+'Obj\'+filename)
else
Model.LoadFromFile(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)))+'Obj\'+filename);
end;此过程用于构造函数,如下所示(TMyObject从TDummy继承):
constructor TMyObject.Create(AOwner:TComponent)
begin
inherited;
Model:=TModel3D.Create(Self);
Model.Parent:=Self;
LoadModel('Object1.obj');
end;在组件的主机项目即将编译时,是否仍然要防止使用ToolsAPI库?
我只是在想类似的指令,如下所示。
{$IFDEF DESIGNTIME}
uses ToolsAPI;
{$ENDIF}但做这种事有可能吗?
发布于 2014-05-21 13:39:06
听起来好像您正在尝试将设计时代码编译到一个运行时项目中。运行时包或可执行文件。这是不允许的您根本无法将任何ToolsAPI单元编译成一个不是设计时包的项目。
当然可以使用条件编译来排除ToolsAPI单元,但是您必须定义自己的条件定义。没有内置的条件,将满足您的需要。
但是使用条件编译可能不是最好的解决方案。通常,您会将使用Tools API的代码分离为不同的单元,并且只在设计时项目中包含这些单元。
因此,您的组件的代码将被分成两个单元。第一个单元,比如uMyComp.pas,包含大部分代码。此单元声明组件并提供其实现。uMyComp.pas中没有任何对ToolsAPI的引用。第二个单元,uMyCompReg.pas说,执行组件注册和任何其他需要ToolsAPI的任务。这些单元之间存在依赖关系,因为uMyCompReg.pas使用uMyComp.pas。然后,您的设计时包将包括两个单元,任何其他不是设计时间的项目都将只包括uMyComp.pas。
你可以用条件词达到同样的效果。设计时项目将定义一个条件,以表明这是设计时间。因此,项目设置可能包括一个名为DESIGNTIME的条件的定义。然后,您的组件的所有代码将驻留在一个名为uMyComp.pas的单元中。任何与设计时间相关的代码都将以DESIGNTIME为条件。而包含uMyComp.pas的任何其他项目都不会定义DESIGNTIME,因此只会省略设计时的代码。
虽然这是可能的,但我认为这并不是解决问题的最佳方法。实际上,如果您查看大量的开放源代码组件开发示例,如果您发现使用条件来处理设计时代码与运行时代码的分离,我会感到惊讶。
如何将ToolsAPI代码划分为设计时间单元?下面是问题的方法:
procedure TMyObject.LoadModel(fileName: string);
begin
if csDesigning in ComponentState then
Model.LoadFromFile(IncludeTrailingPathDelimiter(
ExtractFilePath(GetActiveProject.FileName))+'Obj\'+filename)
else
Model.LoadFromFile(IncludeTrailingPathDelimiter(
ExtractFilePath(ParamStr(0)))+'Obj\'+filename);
end;首先,让我们看看这段代码的共性。首先要注意的是,调用LoadFromFile的外部是相同的。只有在中间,目录的选择,才会有变化。让我们这样写:
procedure TMyObject.LoadModel(fileName: string);
var
ModelDir: string;
begin
if csDesigning in ComponentState then
ModelDir := ExtractFilePath(GetActiveProject.FileName)
else
ModelDir := ExtractFilePath(ParamStr(0));
Model.LoadFromFile(IncludeTrailingPathDelimiter(ModelDir)+'Obj\'+filename);
end;问题是如何将GetActiveProject.FileName移动到设计时代码中。为此,需要使用依赖项注入(DI)。允许另一方提供逻辑。您需要让TMyObject不了解这个特定的细节。您可以使用DI框架来完成这一任务,但这可能是一个小重量级的任务。因此,让我们声明一个包含函数指针的类变量:
type
TMyObject = class(...)
...
public
class var GetModelDir: TFunc<string>;
end;此功能点允许类外部的其他各方指定模型目录的位置。现在,LoadModel变成:
procedure TMyObject.LoadModel(fileName: string);
var
ModelDir: string;
begin
if Assigned(GetModelDir) then
ModelDir := GetModelDir()
else
ModelDir := ExtractFilePath(ParamStr(0));
Model.LoadFromFile(IncludeTrailingPathDelimiter(ModelDir)+'Obj\'+filename);
end;此时,您的代码现在可以在设计时包之外使用。下一步是添加代码,以便在设计时指定GetModelDir。此代码只在设计时注册组件的单元。代码的明显位置在该单元的初始化部分。看起来是这样的:
initialization
TMyObject.GetModelDir :=
function: string
begin
Result := GetActiveProject.FileName;
end;我在这里使用过匿名方法,但您也可以使用object方法,也可以使用普通的旧函数类型,这取决于Delphi版本。
发布于 2014-05-21 13:43:57
是的,但是最好不要有条件的定义,因为这样会造成比它更多的复杂和限制。
有了上面的包结构,使用新组件的应用程序应该只依赖于运行时单元。
https://stackoverflow.com/questions/23783993
复制相似问题