使用Rtti设置方法字段

时间:2014-04-11 13:07:53

标签: delphi annotations delphi-xe rtti

我正在使用Delphi XE编写基类,这将允许降序类通过应用注释来映射dll方法。但是我得到了一个类型转换错误,这是可以理解的。

本质上,基类应如下所示:

TWrapperBase = class
public
  FLibHandle: THandle;
  procedure MapMethods;
end;

procedure TWrapperBase.MapMethods;
var
  MyField: TRttiField;
  MyAttribute: TCustomAttribute;
  pMethod: pointer;
begin
  FLibHandle := LoadLibrary(PWideChar(aMCLMCR_dll));
  for MyField in TRttiContext.Create.GetType(ClassType).GetFields do
    for MyAttribute in MyField.GetAttributes do
      if MyAttribute.InheritsFrom(TMyMapperAttribute) then
      begin
        pMethod := GetProcAddress(FLibHandle, (MyAttribute as TMyMapperAttribute).TargetMethod);
        if Assigned(pMethod) then
          MyField.SetValue(Self, pMethod); // I get a Typecast error here
end;

下降的课程可能如下所示:

TDecendant = class(TWrapperBase)
private type
  TSomeDLLMethod = procedure(aParam: TSomeType); cdecl;
private
  [TMyMapperAttribute('MyDllMethodName')]
  FSomeDLLMethod: TSomeDLLMethod;
public
  property SomeDLLMethod: TSomeDLLMethod read FSomeDLLMethod;
end;

我可以通过对覆盖'MapMethods'中的每个方法的链接进行硬编码来实现这一点。然而,这将要求每个后代都这样做,我想避免。

我知道在这种情况下使用的TValue将包含一个指针而不是正确的类型(在这种情况下为procedure(aParam: TSomeType); cdecl;)。

我的问题:有没有办法将指针从'GetProcAdress'传递为正确的类型,或直接设置字段(例如使用字段地址'PByte(Self)+ MyField.Offset',你可以用来设置记录属性的值)?

使用旧的Rtti,这可以完成,但仅适用于已发布的属性且没有任何类型检查:

if IsPublishedProp(Self, 'SomeDLLMethod') then
  SetMethodProp(Self, 'SomeDLLMethod', GetProcAddress(FLibHandle, 'MethodName');

2 个答案:

答案 0 :(得分:5)

有两个问题:

首先,您的EInvalidCast是由TValue对类型转换非常严格造成的。您传递Pointer并想要设置TSomeDLLMethod类型的字段。您需要显式传递具有正确类型信息的TValue

if Assigned(pMethod) then
begin
  TValue.Make(@pMethod, MyField.FieldType.Handle, value);
  MyField.SetValue(Self, value);
 end;

现在您将遇到另一个EInvalidCast异常,该异常是由于Rtti.pas的GetInlineSize方法中的XE中的错误而触发的,该异常为tkProcedure类型返回0。我不知道这个版本已修复但在XE5中不再存在。

对于XE,这可以通过使用我之前写的一个单元来修复(我刚刚更新以修复此错误):RttiPatch.pas

我还报告了原始问题,因为指针与过程类型兼容,因此TValue也应处理此问题:http://qc.embarcadero.com/wc/qcmain.aspx?d=124010

答案 1 :(得分:1)

您可以尝试以下方式:

Move(pMethod, PByte(Self) + Field.Offset, SizeOf(Pointer));

PPointer(PByte(Self) + Field.Offset)^ := pMethod;