前言,我好像没说清楚。我想枚举、读取和设置给定变量class var
的所有属性或类属性。TClass
找到 TClass 没有问题 - 它通过了。通过给定属性的存在进行过滤没有问题。问题是 RTTI 只是错过了枚举类字段而不是实例字段的方式。
我想做一个声明性的 DLL 加载器。由于 DLL 在 Windows 中是进程全局的(您不能两次加载相同的 DLL),因此它对应于类级别的字段或属性,而不是即时级别的。
所以我想到了类似的东西 LoadDLL(filename:string; funtions: TClass)
并使用属性来指定要获取的入口点。
然后我碰壁了:虽然TRttiClassRefType
在 Delphi 中有 - 只是无法通过给定的实例或其他任何东西获得它:TRTTIContext.GetType(Class)
返回实例的上下文,而不是元类型。
枚举所有元类型,直到按名称获得所需的元类型......丑陋。而且很脆弱。而且慢。虽然它似乎是TRttiClassRefType
我能找到的唯一真正的代码片段。
那么 - 有没有办法通过附加的一些自定义属性来获取和设置类级变量或属性,找到它们?
下面的测试程序只找到IV和IP,跳过CV和CP...
program Project19;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, RTTI;
type
DLL_Info = class (TCustomAttribute) end;
{$M+} {$RTTI EXPLICIT FIELDS ([vcPublic]) PROPERTIES ([vcPublic])}
DLL_Functions = class
public
[DLL_Info] var IV: pointer; // type is just stub
[DLL_Info] class var CV: pointer;
[DLL_Info] property IP: pointer read IV;
[DLL_Info] class property CP: pointer read CV;
end;
{$M-}
var df: DLL_Functions;
procedure SetDLLFunctions;
var
LContext: TRttiContext;
LType: TRttiType;
LVar: TRttiField;
LProp: TRttiProperty;
LAttr: TCustomAttribute;
DLL_fn_Entry, DLL_fn_Optional: boolean;
DLL_fn_Name: string;
fn_ptr: Pointer;
begin
LContext := TRttiContext.Create;
try
LType := LContext.GetType(DLL_Functions);
for LVar in LType.GetFields do begin
Writeln(Lvar.Name);
end;
for LProp in LType.GetProperties do begin
Writeln(LProp.Name);
end;
finally
LContext.Free;
end;
end;
begin
try
df := DLL_Functions.Create;
df.IV := @SetDLLFunctions; // imitating DLL quering by GetProcAddress
df.CV := @SetDLLFunctions;
SetDLLFunctions;
df.Destroy;
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
一些可能的解决方法:
使用
record
登记入口点需要传递两个指针:记录本身和TypeInfo
. 这是多余的,可能会变得不连贯。创建类实例只是为了保存静态指针将是调用时额外的间接级别,并且将是一个需要跟踪的冗余实体。